暑假集训CSP提高模拟4

image
据说我的 \(T2\) 乱搞硬控学长一上午?

A. White and Black

原题:[ARC148C] Lights Out on Tree

对于挨着的颜色相反的节点,肯定要每个点都转一次,手摸一下会发现,只要节点与父亲节点颜色不同就会产生一次贡献,但每

\(dfs\) 直接扫 \(O(n)\)\(T\) ,所以我们需要去记录一下每个节点的儿子数,会发现对于节点和非父亲的祖先节点同时转变,每对

会比父子节点同时翻转多两次操作,所以我们把要翻转的点的父亲节点儿子数减2,节点的儿子数(白点)加点集数(黑点)即

为答案,多测记得把儿子复原

点击查看代码
#include<bits/stdc++.h>
const int maxn=2e5+10;
using namespace std;
int n,q,m,cnt[maxn],fa[maxn],v[maxn]; 

int main()
{ 
	scanf("%d%d",&n,&q);
	for(int i=2,x;i<=n;i++)
	{
		scanf("%d",&fa[i]);
		cnt[fa[i]]++;
	}
	while(q--)
	{
		scanf("%d",&m);
		int ans=m;
		for(int i=1;i<=m;i++) scanf("%d",&v[i]),cnt[fa[v[i]]]-=2;
		for(int i=1;i<=m;i++) ans+=cnt[v[i]];
		for(int i=1;i<=m;i++) cnt[fa[v[i]]]+=2;
		printf("%d\n",ans);	
	} 

	return 0;
} 
/*
5 1
1 2 3 4
2 1 5
*/

B. White and White

原题:Encryption (hard)

当时 \(T1\) 暴力太费时,导致没时间了, \(T2\) 胡了个特殊性质又对着模数想着骗点分,然后就硬控学长两小时,据说一开始好像

\(A\) 了。。。后来学长被迫改数据又加了个捆绑测试给我卡了。这个乱胡的算法原理实际上是,在极多数中分很小的块数求和再

膜一个很小的模数,极大概率可以找到很多膜模数接近于0的块,所以答案极大概率是比模数小的,因为模数很小,样本很多,

所以膜出来的数也有相当多的是重复的,所以加起来直接膜是可以骗到很多分的

正解:区间\(dp\) ,用一个数组记录到目前的最小答案的下标,作为后面求解时的断点

点击查看代码
#include<bits/stdc++.h>
const int maxn=5e5+10;
using namespace std;
int n,k,p,a[maxn],sum[maxn],f[maxn][110],g[2][110]; 
int main()
{
	scanf("%d%d%d",&n,&k,&p);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum[i]+=a[i]+sum[i-1];
		sum[i]%=p; 
	}
	memset(f,0x7f7f7f,sizeof f);
	f[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=k;j++)f[i][j]=f[g[i&1][j-1]][j-1]+((sum[i]-sum[g[i&1][j-1]])%p+p)%p;
		for(int j=1;j<=k;j++)g[i&1^1][j]=f[i][j]<f[g[i&1][j]][j]?i:g[i&1][j];
	}
	printf("%d",f[n][k]);
	
	return 0;
} 

C. Black and Black

原题:[ARC153C] ± Increasing Sequence

这题写的时候也挺抽象的,本来是冲着那 \(20pts\) 的部分分去的,然后自己开始手模找规律,结果好像把正解写出来了。。。

赛事写时判无解时出了点小锅,就那20分部分分没拿到。。。

对于首尾元素是什么来考虑,分为首尾分别 \(1/-1\) 来考虑,里面再分奇偶考虑,首尾相同和 \(n\) 为奇数时肯定有解

对于构造,我们先让其由中间为0,按递增向两边差为一的填数,把首尾是一的位置留一个出来,用0减去现在填了的值

看是否合法,合法就输出,不合法就从n向前找一个位置让这个段的 \(a\) 序列的和为 \(1/-1\) (1在首就找为1的,反之则-1)

找不到就无解,然后一个让序列同时加,直到最后一个数合法了即可。

但有可能要填的数都比0要小,这样填出来相当于和比0多了\(sum(n)\)的整数倍(整个序列同时加的),所以判时还要判一个填

后的和是否大于零,\(sum(n)\)是不是等于0,都满足才是无解

点击查看代码
#include<bits/stdc++.h>
const int maxn=2e5+10;
using namespace std;
int n,a[maxn],b[maxn],sum[maxn];

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	if(n==1)
	{
		printf("Yes\n"); 
		printf("0\n");
		return 0;
	} 
	if(a[1]==1&&a[n]==1)
	{
		if(n&1)
		{
			b[(n+1)>>1]=0;
			for(int i=((n+1)>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
			for(int i=((n+1)>>1)+1;i<n;i++) b[i]=b[i-1]+1;
			int temp=0;
			for(int i=1;i<n;i++) temp+=a[i]*b[i];
			temp=-temp;
			if(temp<=b[n-1])
			{
				while(temp<=b[n-1])
				{
					temp++,b[1]--;
				}
			} 
			b[n]=temp;
			printf("Yes\n");
			for(int i=1;i<=n;i++)
				printf("%d ",b[i]);
		}
		else
		{
			b[n>>1]=0,b[(n>>1)+1]=1;
			for(int i=(n>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
			for(int i=(n>>1)+2;i<n;i++) b[i]=b[i-1]+1;
			int temp=0;
			for(int i=1;i<n;i++) temp+=a[i]*b[i];
			temp=-temp;
			if(temp<=b[n-1])
			{
				while(temp<=b[n-1])
				{
					temp++,b[1]--;
				}
			} 
			b[n]=temp;
			printf("Yes\n");
			for(int i=1;i<=n;i++)
				printf("%d ",b[i]);
		}
	}
	
	if(a[1]==-1&&a[n]==-1)
	{
		if(n&1)
		{
			b[(n+1)>>1]=0;
			for(int i=((n+1)>>1)-1;i>1;i--) b[i]=b[i+1]-1;
			for(int i=((n+1)>>1)+1;i<=n;i++) b[i]=b[i-1]+1;
			int temp=0;
			for(int i=2;i<=n;i++) temp+=a[i]*b[i];
			if(temp>=b[2])
			{
				while(temp>=b[2])
				{
					temp--,b[n]++;
				}
			} 
			b[1]=temp;
			printf("Yes\n");
			for(int i=1;i<=n;i++)
				printf("%d ",b[i]);
		}
		else
		{
			b[n>>1]=0,b[(n>>1)+1]=1;
			for(int i=(n>>1)-1;i>1;i--) b[i]=b[i+1]-1;
			for(int i=(n>>1)+2;i<=n;i++) b[i]=b[i-1]+1;
			int temp=0;
			for(int i=2;i<=n;i++) temp+=a[i]*b[i];
			if(temp>=b[2])
			{
				while(temp>=b[2])
				{
					temp--,b[n]++;
				}
			} 
			b[1]=temp;
			printf("Yes\n");
			for(int i=1;i<=n;i++)
				printf("%d ",b[i]);
		}
	} 
	
	if(a[1]==-1&&a[n]==1)
	{
		if(n&1)
		{
			b[(n+1)>>1]=0;
			for(int i=((n+1)>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
			for(int i=((n+1)>>1)+1;i<n;i++) b[i]=b[i-1]+1;
			int temp=0,l=0,z=-1;
			for(int i=1;i<n;i++) temp+=a[i]*b[i];
			temp=-temp;
			if(temp<=b[n-1])
			{
				for(int i=n-1;i>=0;i--)
				{
					if(sum[n]-sum[i]<0)
					{
						z=i;
						break;
					}
				}
				while(temp<=b[n-1])
				{
					temp++;
					l++;
				}
			} 
			b[n]=temp;
			printf("Yes\n");
			int summ=0; 
			for(int i=1;i<=n;i++)
			{
				if(i>z) b[i]+=l;
				summ+=b[i]*a[i];
			}
			if(summ!=0)
			{
				summ=summ/sum[n];
			}
			for(int i=1;i<=n;i++)
			{
				printf("%d ",b[i]-summ);
			}	
		}
		else
		{
			b[n>>1]=0,b[(n>>1)+1]=1;
			for(int i=(n>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
			for(int i=(n>>1)+2;i<n;i++) b[i]=b[i-1]+1;
			int temp=0,l=0,z=-1;
			for(int i=1;i<n;i++) temp+=a[i]*b[i];
			temp=-temp;
			if(temp<=b[n-1])
			{
				for(int i=n-1;i>=0;i--)
				{
					if(sum[n]-sum[i]<0)
					{
						z=i;
						break;
					}
				}
				while(temp<=b[n-1])
				{
					temp++;
					l++;
				}
			} 
			b[n]=temp;
			
			int summ=0;
			for(int i=1;i<=n;i++)
			{
				if(i>z) b[i]+=l;
				summ+=b[i]*a[i];
			}
			if(z==-1&&summ!=0&&!sum[n])
			{
				printf("No\n");
				return 0;
			}
			
			if(summ!=0)
			{
				summ=summ/sum[n];
			}
			
			printf("Yes\n");
			
			for(int i=1;i<=n;i++)
			{
				printf("%d ",b[i]-summ);
			}
		}
	} 
	
	if(a[1]==1&&a[n]==-1)
	{
		if(n&1)
		{
			b[(n+1)>>1]=0;
			for(int i=((n+1)>>1)-1;i>1;i--) b[i]=b[i+1]-1;
			for(int i=((n+1)>>1)+1;i<=n;i++) b[i]=b[i-1]+1;
			int temp=0,l=0,z=-1;
			for(int i=2;i<=n;i++) temp+=a[i]*b[i];
			temp=-temp;
			if(temp>=b[2])
			{
				for(int i=n-1;i>=0;i--)
				{
					if(sum[n]-sum[i]>0)
					{
						z=i;
						break;
					}
				}
				while(temp>=b[2])
				{
					temp--;
					l++;
				}
			} 
			b[1]=temp;
			printf("Yes\n");
			int summ=0; 
			for(int i=1;i<=n;i++)
			{
				if(i>z) b[i]+=l;
				summ+=b[i]*a[i];
			}
			if(summ!=0)
			{
				summ=summ/sum[n];
			}
			for(int i=1;i<=n;i++)
			{
				printf("%d ",b[i]-summ);
			}
				
		}
		else
		{
			b[n>>1]=0,b[(n>>1)+1]=1;
			for(int i=(n>>1)-1;i>1;i--) b[i]=b[i+1]-1;
			for(int i=(n>>1)+2;i<=n;i++) b[i]=b[i-1]+1;
			int temp=0,l=0,z=-1;
			for(int i=2;i<=n;i++) temp+=a[i]*b[i];
			temp=-temp;
			if(temp>=b[2])
			{
				for(int i=n-1;i>=0;i--)
				{
					if(sum[n]-sum[i]>0)
					{
						z=i;
						break;
					}
				}
				
				while(temp>=b[2])
				{
					temp--;
					l++;
				}
			} 
			b[1]=temp;
			
			int summ=0;
			for(int i=1;i<=n;i++)
			{
				if(i>z) b[i]+=l;
				summ+=b[i]*a[i];
			}
			if(z==-1&&summ!=0&&!sum[n])
			{
				printf("No\n");
				return 0;
			}
			
			if(summ!=0)
			{
				summ=summ/sum[n];
			}
			
			printf("Yes\n");
			for(int i=1;i<=n;i++)
			{
				printf("%d ",b[i]-summ);
			}
		}
	} 
	
	
	return 0;
} 

/*
7
-1 -1 1 1 -1 1 1
*/

D. Black and White

原题:[ZJOI2007] 捉迷藏

首先dfs整棵树一遍,进入一个节点的时候加上一个左括号,然后是节点编号,当这个节点的所有子树遍历完后再添上一个右括号,这

就是括号序列。两个点的距离就是两点编号中去除可配对括号剩下的括号数。用线段树去维护

我们记右括号数为a,左括号数为b。左区间右左括号数记为\(a1,b1\),右区间\(a2,b2\)

\(dis=a1+abs(b1-a2)+b2=max((a1+b1)+(b2-a2),(a1-b1)+(a2+b2))\)

显然\(max(a1+b1),max(b2-a2)\)这种是可以单独维护的。

因此记录区间前缀的\(max(a+b),max(b-a)\),后缀的\(max(a+b),max(a-b)\)即可,用l,r分别表示,

点击查看代码
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int maxn=1e5+10;
int num,s[maxn<<2],pos[maxn<<4],head[maxn],to[maxn<<1],nxt[maxn<<1],n,m,cnt,tot;
bool c[maxn];

void add(int x,int y)
{
	to[++num]=y;
	nxt[num]=head[x];
	head[x]=num;
}

struct lsx
{
	int a,b,l1,l2,r1,r2,dis; 
}tr[maxn<<4];

void dfs(int u,int fa)
{
	s[++tot]=-1;
	s[++tot]=u;
	pos[u]=tot;
	for(int i=head[u];i;i=nxt[i])
	{
		int y=to[i];
		if(y==fa)continue;
		dfs(y,u);
	}
	s[++tot]=-2;
}

int max(int a,int b){return a>b?a:b;}

void merge(int id)
{
	if(tr[lid].b>tr[rid].a)
	 	tr[id].a=tr[lid].a,tr[id].b=tr[lid].b-tr[rid].a+tr[rid].b;
	else
	 	tr[id].a=tr[lid].a+tr[rid].a-tr[lid].b,tr[id].b=tr[rid].b;
	tr[id].l1=max(tr[lid].l1,max(tr[rid].l1+tr[lid].a-tr[lid].b,tr[rid].l2+tr[lid].a+tr[lid].b));
	tr[id].l2=max(tr[lid].l2,tr[rid].l2-tr[lid].a+tr[lid].b);
	tr[id].r1=max(tr[rid].r1,max(tr[lid].r1-tr[rid].a+tr[rid].b,tr[lid].r2+tr[rid].a+tr[rid].b));
	tr[id].r2=max(tr[rid].r2,tr[lid].r2+tr[rid].a-tr[rid].b);
	tr[id].dis=max(max(tr[lid].r1+tr[rid].l2,tr[lid].r2+tr[rid].l1),max(tr[lid].dis,tr[rid].dis));
}

void build(int id,int l,int r)
{
	if(l==r)
	{
		tr[id].a=tr[id].b=0;tr[id].l1=tr[id].l2=tr[id].r1=tr[id].r2=tr[id].dis=-1e9;
		if(s[l]==-1)tr[id].b=1;
		else if(s[l]==-2)tr[id].a=1;
		else if(!c[s[l]])tr[id].l1=tr[id].r1=tr[id].r2=tr[id].l2=0;
		return;
	}
	int mid=l+r>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	merge(id);
}
void modify(int id,int l,int r,int x)
{
	if(l==r)
	{
		tr[id].a=tr[id].b=0;tr[id].l1=tr[id].l2=tr[id].r1=tr[id].r2=tr[id].dis=-1e9;
		if(s[l]==-1)tr[id].b=1;
		else if(s[l]==-2)tr[id].a=1;
		else if(!c[s[l]])tr[id].l1=tr[id].r1=tr[id].r2=tr[id].l2=0;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)modify(lid,l,mid,x);
	else modify(rid,mid+1,r,x);
	merge(id);
}

int main()
{
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v),
		add(v,u);
	}
	dfs(1,0);
	cnt=n;
	build(1,1,tot); 
	scanf("%d",&m);
	for(int i=1,x;i<=m;i++)
	{
		char s[2];
		scanf("%s",s);
		if(s[0]=='C') 
		{
			scanf("%d",&x),cnt+=c[x]?1:-1;
			c[x]^=1,modify(1,1,tot,pos[x]);
		}
		else if(cnt==0)printf("-1\n");
		else if(cnt==1)printf("0\n");
		else printf("%d\n",tr[1].dis);
	}
}
posted @ 2024-07-22 10:25  _君の名は  阅读(24)  评论(2编辑  收藏  举报