省选模拟14
属实这场考试是顺了我的心意
考场200整,第二题挂掉了10分,第三题的暴力dp没有想到......
第一题不知道我咋了,竟然成功的剪掉了状态然后切掉了
第二题期望数据是随的,于是我打了个暴力,期望复杂度\(\mathcal{O(nlog^2n)}\),最劣复杂度\(\mathcal{O(n^2logn)}\)
第三题抓住了一点点性质,但是想偏了,于是只打了暴搜
T1 好/good
区间dp,这个算法是非常显然的
假设我们已经得到了每个消掉的区间的最大值,那么我们由一个简单的dp就可以得到最后的答案
接下来考虑如何进行区间dp,需要考虑的就是如何把一堆小区间合并
有一个非常非常暴力的思路就是对于一个大区间,我们在这个区间内再做一个dp,也就是dp套dp,总复杂度是\(\mathcal{O(n^5)}\)
我们去观察性质,首先发现当前消掉的一定是一个先上升后下降的序列,也就是说整个序列可以被分成两段
那么这就启发我们可以在顶点处合并答案,题解做法是正反分别做一个上升序列的dp,最后合并就行了
我的也是大同小异,只是把这个需要现成处理的dp用原来的区间dp代表了
最后一维,0表示当前区间最后删掉的那个是上升的,1表示是下降的,2表示拐了,3表示前面三个的并
那么我们只需要枚举在哪个点合并,然后长度的话,我们已经知道了左右端点并且只有单调上升和单调下降可合并,所以长度减一减就有了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
#define mk(x,y) make_pair(x,y)
#define fi first
#define se second
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int inf=0x3f3f3f3f;
const int N=405;
int n,a[N],v[N];
int dp[N][N][5],f[N];
signed main(){
freopen("good.in","r",stdin);
freopen("good.out","w",stdout);
n=read();
fo(i,1,n)v[i]=read();
fo(i,1,n)a[i]=read();
fo(i,0,n)fo(j,0,n)fo(k,0,3)dp[i][j][k]=-inf;
fo(i,1,n)dp[i][i-1][3]=0;
fo(i,1,n)dp[i][i][0]=dp[i][i][1]=dp[i][i][2]=dp[i][i][3]=v[1];
fo(len,2,n)fo(l,1,n-len+1){
int r=l+len-1,tmp=dp[l+1][r-1][3];
if(tmp!=-inf){
if(a[l]+1==a[r])dp[l][r][0]=max(dp[l][r][0],tmp+v[2]);
if(a[r]+1==a[l])dp[l][r][1]=max(dp[l][r][1],tmp+v[2]);
}
fo(i,l+1,r-1){
if(dp[l][i][0]!=-inf){
if(dp[i][r][0]!=-inf){
dp[l][r][0]=max(dp[l][r][0],dp[l][i][0]+dp[i][r][0]-v[a[i]-a[l]+1]-v[a[r]-a[i]+1]+v[a[r]-a[l]+1]);
}
if(dp[i][r][1]!=-inf)dp[l][r][2]=max(dp[l][r][2],dp[l][i][0]+dp[i][r][1]-v[a[i]-a[l]+1]-v[a[i]-a[r]+1]+v[a[i]-a[l]+a[i]-a[r]+1]);
}
if(dp[l][i][1]!=-inf&&dp[i][r][1]!=-inf){
dp[l][r][1]=max(dp[l][r][1],dp[l][i][1]+dp[i][r][1]-v[a[l]-a[i]+1]-v[a[i]-a[r]+1]+v[a[l]-a[r]+1]);
}
}
dp[l][r][3]=max(dp[l][r][0],max(dp[l][r][1],dp[l][r][2]));
fo(i,l+1,r)if(dp[l][i-1][3]!=inf&&dp[i][r][3]!=-inf)dp[l][r][3]=max(dp[l][r][3],dp[l][i-1][3]+dp[i][r][3]);
}
memset(f,-0x3f,sizeof(f));f[0]=0;
fo(i,1,n){
f[i]=max(f[i],f[i-1]);
fo(j,1,i){
if(dp[j][i][3]==-inf)continue;
f[i]=max(f[i],f[j-1]+dp[j][i][3]);
}
}
printf("%d",f[n]);
return 0;
}
T2 色/color
这个还没有写正解,于是把暴力的代码贴在这里
原理就是最短路径只是一条边,也就是说我只要维护两端颜色不同的边的边权的最小值就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
#define mk(x,y) make_pair(x,y)
#define fi first
#define se second
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int N=3e5+5;
int n,m,c,q,col[N];
struct D{int x,y,val;}d[N];
struct E{int to,nxt,id;}e[N*2];
int head[N],rp;
void add_edg(int x,int y,int z){
e[++rp].to=y;e[rp].nxt=head[x];
e[rp].id=z;head[x]=rp;
}
set<pa> st;
signed main(){
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
n=read();m=read();c=read();q=read();
fo(i,1,m){
d[i].x=read();d[i].y=read();d[i].val=read();
add_edg(d[i].x,d[i].y,i);
add_edg(d[i].y,d[i].x,i);
}
fo(i,1,n)col[i]=read();
fo(i,1,m)if(col[d[i].x]!=col[d[i].y])st.insert(mk(d[i].val,i));
while(q--){
int x=read(),y=read();
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
if(col[x]==y)continue;
if(col[v]==y)st.erase(mk(d[e[i].id].val,e[i].id));
if(col[v]==col[x])st.insert(mk(d[e[i].id].val,e[i].id));
}col[x]=y;
printf("%d\n",st.begin()->fi);
}
return 0;
}
T3 乐/music
设f[i]表示长度为i的合法序列的方案数
转移的时候就枚举最短的border就好了,如何保证最短?它本身是一个没有border的串,也就是我们的f
那么我们用分治ntt优化转移就行了,注意这里不太一样,我们保证i<=n/2
如果用原来的那种用左边转移到右边那就不好限制了,完全不可行
所以我们用左区间乘右区间贡献到区间外
还有一个优化,就是只需要分治前一半,最后再乘一下右区间就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int N=1<<22;
const int mod=998244353;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,v[N],ans;
int sum[N],inv[N],f[N];
int w[N],af[N],lim,len;
int mo(int x){return x>=mod?x-mod:x;}
void ntt(int *a,int lim){
fo(i,0,lim-1)if(af[i]>i)swap(a[i],a[af[i]]);
for(int t=lim>>1,d=1;d<lim;d<<=1,t>>=1)
for(int i=0;i<lim;i+=(d<<1))
fo(j,0,d-1){
int tmp=w[t*j]*a[i+j+d]%mod;
a[i+j+d]=mo(a[i+j]-tmp+mod);
a[i+j]=mo(a[i+j]+tmp);
}
}
int a[N],b[N];
void sol(int l,int r){
if(l==r){
f[l]=(sum[l]-f[l]+mod)%mod*inv[l]%mod;
if(l*2<=n)f[l*2]=(f[l*2]+f[l]*sum[l])%mod;
return ;
}
int mid=l+r>>1,m=r-l+mid-l;
sol(l,mid);
fo(i,0,mid-l)a[i]=f[l+i];
fo(i,mid-l+1,r-l)b[i]=sum[l+i];
for(lim=1,len=0;lim<=m;lim<<=1,len++);
fo(i,0,lim-1)af[i]=(af[i>>1]>>1)|((i&1)<<(len-1));
w[0]=1;w[1]=ksm(3,(mod-1)/lim);
fo(i,2,lim-1)w[i]=w[i-1]*w[1]%mod;
ntt(a,lim);ntt(b,lim);
w[0]=1;w[1]=ksm(w[1],mod-2);
fo(i,2,lim-1)w[i]=w[i-1]*w[1]%mod;
fo(i,0,lim-1)a[i]=a[i]*b[i]%mod;
ntt(a,lim);int iv=ksm(lim,mod-2);
fo(i,mid-l+1,min(n-2*l,r+mid-2*l))f[i+2*l]=mo(f[i+2*l]+a[i]*iv%mod);
fo(i,0,lim-1)a[i]=b[i]=0;
sol(mid+1,r);
}
signed main(){
freopen("music.in","r",stdin);
freopen("music.out","w",stdout);
n=read();sum[0]=1;
fo(i,1,n){
v[i]=read();sum[i]=sum[i-1]*v[i]%mod;
inv[i]=ksm(sum[i],mod-2);
}
sol(1,n>>1);
int m=n+(n>>1),l=1,mid=n>>1,r=n;
fo(i,0,mid-l)a[i]=f[l+i];
fo(i,mid-l+1,r-l)b[i]=sum[l+i];
for(lim=1,len=0;lim<=m;lim<<=1,len++);
fo(i,0,lim-1)af[i]=(af[i>>1]>>1)|((i&1)<<(len-1));
w[0]=1;w[1]=ksm(3,(mod-1)/lim);
fo(i,2,lim-1)w[i]=w[i-1]*w[1]%mod;
ntt(a,lim);ntt(b,lim);
w[0]=1;w[1]=ksm(w[1],mod-2);
fo(i,2,lim-1)w[i]=w[i-1]*w[1]%mod;
fo(i,0,lim-1)a[i]=a[i]*b[i]%mod;
ntt(a,lim);int iv=ksm(lim,mod-2);
fo(i,mid-l+1,min(n-2*l,r+mid-2*l))f[i+2*l]=mo(f[i+2*l]+a[i]*iv%mod);
fo(i,0,lim-1)a[i]=b[i]=0;
printf("%lld",mo(sum[n]-f[n]+mod));
return 0;
}