【NOI2019十二省联合省选】部分题简要题解

Day 1 T1 异或粽子

题意:给出一个长为n的序列,选择K个不完全重合的区间使得每个区间的异或值的总和最大。

题解:先做一个前缀异或和,对于每一个右端点我们记录三元组(l,r,x)表示在左端点在\([l,r]\)内,最大异或值为x,塞进堆里。每次取出堆顶,并将该三元组对应的区间分裂成两个,重新扔回堆里。计算区间最大异或值利用可持久化字典树。
时间复杂度\(O(n\log n+K\log Maxvalue)\)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 500005
#define M 17000005
#define L 33
#define LL long long
using namespace std;
LL a[N],cf[L],ans,d[N];
int t[M][2],rt[N],mw,mx[M];
int n,cnt,n1;

void ins(int k,int x,int w)
{
	fod(i,mw,0)
	{
		int p=((a[w]&cf[i])>0);
		t[k][p^1]=t[x][p^1];
		mx[k]=w;
		k=t[k][p]=++n1,x=t[x][p];
	}
	mx[k]=w;
}
void read(LL &x)
{
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
}
struct node
{
	int x,l,r,w;
	LL v;
	friend bool operator <(node x,node y)
	{
		return x.v<y.v;
	}
};
priority_queue<node> h;

int get(int l,int r,LL v)
{
	int k=rt[r];
	fod(i,mw,0)
	{
		int p=(v&cf[i])>0;
		k=(t[k][1-p]&&mx[t[k][1-p]]>=l)?t[k][1-p]:t[k][p];
	}
	return mx[k];
}
int main()
{
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	cin>>n>>cnt;
	cf[0]=1;
	fo(i,1,32) cf[i]=cf[i-1]*(LL)2;
	rt[0]=++n1;
	mw=0;
	fo(i,1,n)
	{
		read(a[i]);
		a[i]^=a[i-1];
		fod(j,31,0) if(a[i]&cf[j]) {mw=max(mw,j);break;} 	
	}
	
	ins(1,0,0);
	fo(i,1,n)
	{
		rt[i]=++n1;
		ins(rt[i],rt[i-1],i);
		
		int w=get(0,i-1,a[i]);
		h.push((node){i,0,i-1,w,a[i]^a[w]});
	}
	
	fo(i,1,cnt)
	{
		node p=h.top();h.pop();
		ans+=p.v;
		int x=p.x;
		if(p.l<p.w) 
		{
			int w=get(p.l,p.w-1,a[x]);
			h.push((node){x,p.l,p.w-1,w,a[x]^a[w]});
		}
		if(p.r>p.w)
		{
			int w=get(p.w+1,p.r,a[x]);
			h.push((node){x,p.w+1,p.r,w,a[x]^a[w]});
		}
	}
	printf("%lld\n",ans);
}


Day 1 T2 字符串问题

题意:给出一个字符串S,将S中的一些子串定为A串,一些定为B串,给出了m组某个A串支配某个B串的关系,要求选出一个最长的字符串T,T为A串拼接而成,且任意相邻的两个A串(ai,ai+1)中,存在一个B串,它是ai+1的前缀,且被ai支配。求最长的长度,需要判是否无限长。

题解:将倒着做一遍,将后缀自动机的fail树弄出来(后缀树),然后我们将包含A,B串的节点设为关键点,将A,B串在这个节点上对应的长度分裂成一个新节点,同一个节点分裂出来的点从长度小到大连边,显然这个新构出来的树点数还是是\(O(n)\)的,将支配关系也看做边连起来,最后新建一个节点,每个A串向这个点连边表示结束。跑拓扑序DP即可,如果出环就是无限长。
分裂节点、找节点的时候利用map和倍增来做,总的时间复杂度\(O(n\log n)\)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 900005
#define M 2000005
#define LL long long
using namespace std;
int t[N][26],n1,mx[N],ft[N][20],n,m,ma,mb,fs[N],nt[M],dt[M],pr[M],m1,mq,pt[2][N],len[N],d[N],rd[N],n2;
LL f[N],ans;
char st[N];
struct node
{
	int x,y,p,w;
	friend bool operator <(node x,node y)
	{
		return (x.x<y.x)||(x.x==y.x&&x.y<y.y);	
	}
}ask[N];
map<int,int> h[N];
typedef map<int,int>::iterator IT;
void link(int x,int y,int z)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;	
	rd[y]++;
	pr[m1]=z;
}
void make()
{
	int ls=1;
	n1=1;
	fo(j,0,25) t[1][j]=0;
	fod(i,n,1)
	{
		int c=st[i]-'a',p=ls;
		mx[ls=++n1]=n-i+1;	
		fo(j,0,25) t[n1][j]=0;	
		while(p&&!t[p][c]) t[p][c]=ls,p=ft[p][0];
		if(p)
		{
			int q=t[p][c];
			if(mx[q]==mx[p]+1) ft[ls][0]=q;
			else
			{
				ft[++n1][0]=ft[q][0];
				mx[n1]=mx[p]+1;
				ft[q][0]=ft[ls][0]=n1;
				fo(j,0,25) t[n1][j]=t[q][j];
				while(p&&t[p][c]==q) t[p][c]=n1,p=ft[p][0];	
			}
		}
		else ft[ls][0]=1;
	}
}
void jump(int &x,int e)
{
	for(int j=19;mx[ft[x][0]]>=e;)
	{
		while(j&&mx[ft[x][j]]<e) j--;
		x=ft[x][j];
	}
}
void read(int &x)
{
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
}
void bfs()
{
	d[0]=0;
	fo(i,1,n2) if(!rd[i]) d[++d[0]]=i;
	int l=0;
	while(l<d[0])
	{
		int k=d[++l];
		ans=max(ans,f[k]);
		for(int i=fs[k];i;i=nt[i])
		{
			int p=dt[i];
			f[p]=max(f[p],f[k]+pr[i]);
			rd[p]--;
			if(rd[p]==0) d[++d[0]]=p;	
		}
	}
}
int main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	int t1;
	cin>>t1;
	while(t1--)
	{
		fo(i,1,n1) 
		{
			mx[i]=0,h[i].clear();
			ft[i][0]=0;
		}
		fo(i,1,n2) fs[i]=rd[i]=f[i]=0;
		m1=0;
		n1=n2=0;
		mq=0;
		scanf("\n%s",st+1); 
		n=strlen(st+1);
		make();
		
		fo(i,1,n1) h[i][mx[i]]=i;
		read(ma);
		fo(i,1,ma) 
		{
			int l,r;
			read(l),read(r);
			ask[++mq]=(node){l,r,0,i};
			len[i]=r-l+1;
		}
		read(mb);
		fo(i,1,mb)
		{
			int l,r;
			read(l),read(r);
			ask[++mq]=(node){l,r,1,i};
		}
		
		sort(ask+1,ask+mq+1);
		fo(j,1,19)
			fo(i,1,n1) ft[i][j]=ft[ft[i][j-1]][j-1];
		int k=1,j=n,w=1;
		n2=n1;
		ask[mq+1].x=n+1;
		fod(i,mq,1)
		{
			if(i==mq||ask[i].x!=ask[i+1].x)
			{
				fod(p,ask[i+1].x-1,ask[i].x) k=t[k][st[p]-'a'];
				w=k;
			}
			jump(w,ask[i].y-ask[i].x+1);
			if(!h[w][ask[i].y-ask[i].x+1]) h[w][ask[i].y-ask[i].x+1]=++n2;
			pt[ask[i].p][ask[i].w]=h[w][ask[i].y-ask[i].x+1];	
		}
		
		fo(i,1,n1)
		{
			IT it=h[i].begin();
			int ls=ft[i][0];
			for(;it!=h[i].end();it++) 
				if(ls) link(ls,(*it).second,0),ls=(*it).second;
		}
		read(m);
		fo(i,1,m)
		{
			int x,y;
			read(x),read(y);
			link(pt[0][x],pt[1][y],len[x]);
		}
		n2++;
		ans=0;
		fo(i,1,ma) link(pt[0][i],n2,len[i]); 
		
		bfs();
		if(d[0]!=n2) printf("-1\n");
		else printf("%lld\n",ans);
	}
	
}

Day 2 T1 皮配

题意:有n所学校,c座城市,每个学校都属于某一个城市。现在有4位导师,分成两大阵营(1,2),(3,4)和两大派系(1,3),(2,4)。每个阵营和每个派系都有一个人数上限,每个学校有一个选手人数。现在问有多少种分配方案,满足同一个城市的学校选择同一个阵营,同一个学校的所有选手选择同一个导师。此外,有k所学校的选手一定不会选择某一位导师。

题解:若k=0,我们容易发现阵营限制和派系限制是独立的,由于一个城市必须选择相同阵营,且对于学校来说所属城市选哪个阵营没有关系。我们只需要将城市对于阵营人数做背包,学校对于派系做背包,最后答案乘在一起即可。
若k!=0,我们将有限制的k个学校取出,剩余没有限制的学校和城市跟之前一样DP。剩下k所学校暴力做同时两维限制的背包即可,最后再将三个东西乘在一起。
直接做容易卡常数,我们发现每所学校的人数不超过10,那么DP时和的上界对总人数取个min,单组数据复杂度就优化到是\(O((n+c)M+kM*10k)\)

相当丑...

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 2505
#define LL long long
#define mo 998244353
#define L 12
using namespace std;

LL f[N][N],g[N][N],cf[L+1],l2[L+1],h[2][N][N][2];
int a[N],c[N],s[N],t,n,m,lim[4],mx,bp[N],fr[N],n1,m1,d[N],d2[N],bq[2][N][N][2];
bool bz[N];

bool cmp(int x,int y)
{
	return (bp[x]<bp[y]);	
}
bool cmp2(int x,int y)
{
	return (bz[x]<bz[y]);
}
void inc(LL &x,LL v)
{
	x=(x+v)%mo;
}
bool cmp3(int x,int y)
{
	return fr[x]<fr[y];
}
LL gf(int l,int r)
{
	if(r>lim[2]) r=lim[2];
	if(r<0) return 0;
	LL s=f[n1][r];
	if(l>0) s=(s-f[n1][l-1]+mo)%mo;
	return s;
}
LL gg(int l,int r)
{
	if(r>lim[0]) r=lim[0];
	if(r<0||l>r) return 0;
	LL s=g[m1][r];
	if(l>0) s=(s-g[m1][l-1]+mo)%mo;
	return s;
}
int main()
{
	freopen("mentor.in","r",stdin);
	freopen("mentor.out","w",stdout);
	cin>>t;
	cf[0]=1;
	fo(i,1,L) l2[cf[i]=cf[i-1]<<1]=i;
	while(t--)
	{
		mx=0;
		memset(bz,0,sizeof(bz));
		memset(s,0,sizeof(s));
		memset(f,0,sizeof(f));
		memset(h,0,sizeof(h));
		memset(g,0,sizeof(g));
		memset(bq,0,sizeof(bq));
		memset(bp,255,sizeof(bp));
		scanf("%d%d",&n,&m);
		int sum=0;
		fo(j,0,3) scanf("%d",&lim[j]),mx=max(mx,lim[j]);
		
		fo(i,1,n) 
		{
			int x,y;
			scanf("%d%d",&x,&y);
			fr[i]=x;
			sum+=y;
			s[x]+=y,c[i]=y;
			d[i]=i;
		}
		int lc;
		scanf("%d",&lc);
		fo(i,1,lc) 
		{
			int x,y;
			scanf("%d%d",&x,&y);
			bp[x]=y;
			bz[fr[x]]=1;
		}
		sort(d+1,d+n+1,cmp);
		n1=0;
		while(n1<n&&bp[d[n1+1]]<0) n1++;
		f[0][0]=1;
		fo(i,1,n1)
		{
			fo(j,0,lim[2]) 
			{
				f[i][j]=f[i-1][j];
				if(j>=c[d[i]]) inc(f[i][j],f[i-1][j-c[d[i]]]);
			}
		}
		
		fo(i,1,m) d2[i]=i;
		sort(d2+1,d2+m+1,cmp2);
		m1=0;
		while(m1<m&&!bz[d2[m1+1]]) m1++;
		g[0][0]=1;
		fo(i,1,m1)
		{
			fo(j,0,lim[0])
			{
				g[i][j]=g[i-1][j];
				if(s[d2[i]]>0&&j>=s[d2[i]]) inc(g[i][j],g[i-1][j-s[d2[i]]]);
			}
		}
		
		h[0][0][0][0]=1,bq[0][0][0][0]=n1;
		sort(d+n1+1,d+n+1,cmp3);
		int vr=0;
		fo(i,n1+1,n)
		{
			int i1=(i-n1)&1;
			if(i==n1+1||fr[d[i]]!=fr[d[i-1]]) vr+=s[fr[d[i]]];
			fo(p,0,1)
			{
				int r1=min(lim[0],vr);
				fo(j,0,r1)
				{
					int uj=(p==0&&(i==n1+1||fr[d[i]]!=fr[d[i-1]]))?j-s[fr[d[i]]]:j;
					if(uj>=0)
					{
						int r2=min(10*(i-n1),lim[2]);
						fo(k,0,r2)
						{
							h[i1][j][k][p]=0;
							bq[i1][j][k][p]=i;
							if(!(i==n1+1||fr[d[i]]!=fr[d[i-1]]))
							{
								if(bp[d[i]]!=p*2&&k>=c[d[i]]) 
								{
									if(bq[1^i1][uj][k-c[d[i]]][p]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k-c[d[i]]][p]);
								}
								if(bp[d[i]]!=p*2+1) 
								{
									if(bq[1^i1][uj][k][p]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k][p]);
								}
							}	
							else
							{
								if(bp[d[i]]!=p*2&&k>=c[d[i]]) 
								{
									if(bq[1^i1][uj][k-c[d[i]]][0]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k-c[d[i]]][0]);
									if(bq[1^i1][uj][k-c[d[i]]][1]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k-c[d[i]]][1]);
								}
								if(bp[d[i]]!=p*2+1) 
								{
									if(bq[1^i1][uj][k][0]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k][0]);
									if(bq[1^i1][uj][k][1]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k][1]);
								}
							}
						}	
					}	
				}
			}
		}
		
		fo(i,1,lim[2]) inc(f[n1][i],f[n1][i-1]);
		fo(i,1,lim[0]) inc(g[m1][i],g[m1][i-1]);
		int i1=(n-n1)&1;
		LL ans=0;
		fo(j,0,lim[0]) fo(k,0,lim[2])
		{
			if(bq[i1][j][k][0]==n) inc(ans,h[i1][j][k][0]*gg(sum-j-lim[1],lim[0]-j)%mo*gf(sum-k-lim[3],lim[2]-k)%mo);
			if(bq[i1][j][k][1]==n) inc(ans,h[i1][j][k][1]*gg(sum-j-lim[1],lim[0]-j)%mo*gf(sum-k-lim[3],lim[2]-k)%mo);
		}
		printf("%lld\n",ans);
	}
}

Day 2 T2 春节十二响

题意:给出一棵有根树,每个节点有点权,要将所有节点放入若干个集合中,每个集合的代价是集合中点权最大值。要求树上祖先和后代不能放入同一个集合。求最小总代价。

题解:先考虑一条链的情况(根不一定为链端),那一定是长一点的链上每个节点给一个集合,短的链的点塞进这些集合,根节点自成一个集合。深入思考可以发现一定是大的和大的点放在一起,小的和小的放在一起更优。同时这启发我们集合个数与深度有关。
扩展到树上,我们采用长链剖分,对于每条长链维护一个堆记录集合的权值,合并子树的时候就暴力取出长链的前若干大合并,再暴力塞回去。
由长链剖分的时间复杂度分析,这样做的时间复杂度是\(O(n\log n)\)的。

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 200005
#define LL long long
using namespace std;
int fs[N],nt[2*N],dt[2*N],ft[N],n,m,pr[N],dep[N],son[N],m1;
LL ans;
priority_queue<int> h[N];
int rt[N],n1,u1[N];

void link(int x,int y)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;
}
void make(int k,int fa)
{
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa) make(p,k),son[k]=(dep[p]>dep[son[k]])?p:son[k];	
	}	
	dep[k]=dep[son[k]]+1;
}
void dfs(int k,int fa)
{
	if(son[k]) dfs(son[k],k),rt[k]=rt[son[k]];
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa&&p!=son[k]) dfs(p,k);
	}
	
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa&&p!=son[k])
		{
			u1[0]=0;
			while(!h[rt[p]].empty())
			{
				int x=h[rt[k]].top(),y=h[rt[p]].top();
				h[rt[k]].pop(),h[rt[p]].pop();	
				u1[++u1[0]]=max(x,y);
			}	
			fo(j,1,u1[0]) h[rt[k]].push(u1[j]);
		}
	}
	
	if(!rt[k]) rt[k]=++n1;
	h[rt[k]].push(pr[k]);
}
int main()
{
	freopen("spring.in","r",stdin);
	freopen("spring.out","w",stdout);
	cin>>n;
	fo(i,1,n) scanf("%d",&pr[i]);
	fo(i,2,n) scanf("%d",&ft[i]),link(ft[i],i);
	make(1,0);
	dfs(1,0);
	ans=0;
	while(!h[rt[1]].empty()) 
	{
		ans+=h[rt[1]].top();
		h[rt[1]].pop();
	}
	printf("%lld\n",ans);
}	

posted @ 2019-04-11 15:00  BAJim_H  阅读(697)  评论(0编辑  收藏  举报