省选模拟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;	
}
posted @ 2022-02-12 16:23  fengwu2005  阅读(46)  评论(0编辑  收藏  举报