JZOJ 4235 序列
题目
在n个数中找三个最靠前的数,使它们可以组成一个三角形,这n个数可能会被修改
分析
那么首先很容易想到暴力的方法,没错,这就是暴力,为什么呢,可以想到三角形之和大于第三边,如果要使它不合法且数值最小,那应该是一串斐波那契数列,问题是 f [ 50 ] f[50] f[50]就炸了,所以单次询问的时间复杂度为 O ( 5 0 3 ) O(50^3) O(503),题目过水,代码就不贴了
JZOJ 4226 A
题目
一个 n n n个点, m m m条边的无向图,现在增加最少的边(不能是自环,但可以是重边),使每个点的度至少为 k k k
分析
那么想到的最水的方法,就是求
⌈
(
∑
i
=
1
,
k
>
d
e
g
[
i
]
n
k
−
d
e
g
[
i
]
)
÷
2
⌉
\lceil(\sum_{i=1,k>deg[i]}^nk-deg[i])\div 2\rceil
⌈(i=1,k>deg[i]∑nk−deg[i])÷2⌉
问题是这样会有问题(原谅我考试时没有找到可以卡掉它的数据)

如图所示,
k
k
k为3,那么5是完全被孤立的,理论上应该是
⌈
3
2
⌉
=
2
\lceil\frac{3}{2}\rceil=2
⌈23⌉=2,但是应该是3,所以说还要加一个特判,也就是说如果需求量最大的点超过其它点的总和,那么答案就是需求量最大的点
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int n,m,k,deg[100001];
long long ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
signed main(){
n=iut(); m=iut()<<1; k=iut();
while (m--) ++deg[iut()];
for (rr int i=1;i<=n;++i)
if (deg[i]<k) ans+=k-deg[i];
for (rr int i=1;i<=n;++i)
if (k-deg[i]>=((ans+1)>>1))
return !printf("%lld\n",k-deg[i]);
return !printf("%lld",(ans+1)>>1);
}
JZOJ 4227 B
题目
给定一个长度为 n n n的数组 A A A,以及一个 n × 26 n×26 n×26 的矩阵 w w w,其中 w i , j w_{i, j} wi,j表示第 i i i个位置填第 j j j个小写字母的价值,现在你需要给出一个长度为 n n n的字符串,使得它的后缀数组是 A A A,而且它每个位置的价值和最大
分析
首先可以知道通过它们的排列可以知道它们在原串中的位置,接着就是一道dp题了
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示在原串中位于位置
i
i
i,所填字母为
j
j
j的最大价值和,那么
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
k
]
+
w
[
a
[
i
]
]
[
j
]
f[i][j]=f[i-1][k]+w[a[i]][j]
f[i][j]=f[i−1][k]+w[a[i]][j]
特判:举个例子
a
b
a
b
a
ababa
ababa是可以用相同的字母,那应该怎么办呢,那么
r
a
n
k
[
a
[
i
]
+
1
]
>
=
r
a
n
k
[
a
[
i
−
1
]
+
1
]
rank[a[i]+1]>=rank[a[i-1]+1]
rank[a[i]+1]>=rank[a[i−1]+1]才可以保证合法,为什么呢,
s
[
i
]
=
s
[
i
−
1
]
s[i]=s[i-1]
s[i]=s[i−1],那么只要往后移一位比较就可以了
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
int n,rk[100001],a[100001],w[100001][28],f[2][28],ans; long long ran;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed rando(){
ran=(ran*100000005+1532777326)%998244353;
return ran/100;
}
inline signed max(int a,int b){return a>b?a:b;}
signed main(){
n=iut(); scanf("%lld",&ran);
for (rr int i=1;i<=n;++i) rk[a[i]=iut()]=i;
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<27;++j) w[i][j]=rando()%10000;
for (rr int i=1;i<27;++i) f[1][i]=w[a[1]][i];
for (rr int i=2;i<=n;++i){
memset(f[i&1],0,sizeof(f[i&1]));
for (rr int j=1;j<27;++j)
for (rr int k=1;k<=j;++k){
if (j==k&&rk[a[i]+1]<rk[a[i-1]+1]) continue;
f[i&1][j]=max(f[i&1][j],f[1-(i&1)][k]+w[a[i]][j]);
}
}
for (rr int i=1;i<27;++i) ans=max(ans,f[n&1][i]);
return !printf("%d",ans);
}
JZOJ 4228 C
题目
有 n n n个点,每个点向上下左右四个方向之一连出一条射线,这些射线不能相交且射线不能经过除了发出点之外的其他点,问有多少种方案
分析
这是一道神奇的题目,设
f
[
i
]
[
a
]
[
b
]
[
c
]
[
d
]
f[i][a][b][c][d]
f[i][a][b][c][d]表示第
i
i
i个点,
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d分别为

然后只要注意细节就行了
代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#define rr register
#define g(i,a,b) for (rr int i=a;i<=b;++i)
using namespace std;
const int mod=998244353; int f[2][55][55][55][55],n;
struct site{int x,y;}t[55]; bool v[55][4];
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
bool cmp(site a,site b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
inline void add(int &a,int b){a=(a+b)%mod;}
signed main(){
n=iut(); g(i,1,n) t[i]=(site){iut(),iut()};
sort(t+1,t+1+n,cmp); f[0][0][0][0][0]=1;
g(i,1,n){
v[i][0]=v[i][1]=v[i][2]=v[i][3]=1;
g(j,1,n) if (i!=j){
if (t[i].y==t[j].y&&t[i].x>t[j].x) v[i][0]=0;
if (t[i].y==t[j].y&&t[i].x<t[j].x) v[i][2]=0;
if (t[i].x==t[j].x&&t[i].y<t[j].y) v[i][1]=0;
if (t[i].x==t[j].x&&t[i].y>t[j].y) v[i][3]=0;
}
}
g(i,1,n){
g(a,0,i-1) g(b,0,i-1) g(c,0,i-1) g(d,0,i-1)
if (f[1-(i&1)][a][b][c][d]) g(j,0,3){
if (!j){
if (!v[i][0]) continue;
if ((!c||t[c].y>t[i].y)&&(!d||t[d].y<t[i].y))
add(f[i&1][a][b][c][d],f[1-(i&1)][a][b][c][d]);
}
else if (j==1){
if (!v[i][1]) continue;
if (!b||t[b].y<t[i].y){
rr int xa=a,xb=b,xc=c,xd=d;
if (!c||t[c].y>t[i].y) xc=i;
add(f[i&1][xa][xb][xc][xd],f[1-(i&1)][a][b][c][d]);
}
}
else if (j==2){
if (!v[i][2]) continue;
rr int xa=a,xb=b,xc=c,xd=d;
if (!a||t[i].y<t[a].y) xa=i;
if (!b||t[i].y>t[b].y) xb=i;
add(f[i&1][xa][xb][xc][xd],f[1-(i&1)][a][b][c][d]);
}
else if (v[i][3]){
if (!a||t[a].y>t[i].y){
rr int xa=a,xb=b,xc=c,xd=d;
if (!d||t[d].y<t[i].y) xd=i;
add(f[i&1][xa][xb][xc][xd],f[1-(i&1)][a][b][c][d]);
}
}
}
memset(f[1-(i&1)],0,sizeof(f[1-(i&1)]));
}
rr int ans=0;
g(a,0,n) g(b,0,n) g(c,0,n) g(d,0,n)
add(ans,f[n&1][a][b][c][d]);
return !printf("%d",ans);
}
后续
日渐变菜

本文深入解析了JZOJ平台上的四道经典算法题目,包括寻找构成三角形的数、优化无向图节点度数、后缀数组价值最大化及射线路径计数问题,提供了详细的思路与代码实现。

被折叠的 条评论
为什么被折叠?



