首页 > 其他分享 >暑假集训CSP提高模拟4

暑假集训CSP提高模拟4

时间:2024-07-22 10:29:33浏览次数:13  
标签:lid 集训 int tr CSP summ 暑假 rid id

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

A. White and Black

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

次 \(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

当时 \(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

这题写的时候也挺抽象的,本来是冲着那 \(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

首先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);
	}
}

标签:lid,集训,int,tr,CSP,summ,暑假,rid,id
From: https://www.cnblogs.com/oceansofstars/p/18315379

相关文章

  • 2024暑假集训测试8
    前言比赛链接。爆零了?!?T4莫名CE了,T2因为某些人打乱搞做法使出题人改数据和时限,\(O(npk)\)做法死掉了,主要还是数组开大了还忘了算,直接爆零了。T1WhiteandBlack显然不存在无解,从根开始扫,遇到黑色就翻转,前后顺序不影响结果,该方案为正确且唯一方案。继续观察发现若一个......
  • 阅读笔记《CCSP认证官方指南》第2版
    按需自助服务:允许客户自主扩展计算和存储,而不需要或很少需要提供商的介入或提前沟通。这项服务是实时生效的。入侵检测分析人员必须理解组织在做什么、为什么做、如何做以及在哪里做,以便更好地理解外部攻击的性质和强度,并相应地调整安全基线。云计算平台的标志性特性:弹性、简单......
  • 2024暑假集训测试7
    前言比赛链接。终于不挂分了这次,但是T2写得太慢了导致T4没写完只能胡暴力。但是赛时数据和样例出了好多问题给不少人造成了影响。T1abc猜想\(ans=\lfloor\dfrac{a^b}{c}\rfloor\bmodc=\dfrac{a^b-a^b\bmodc}{c}\bmodc\)不妨设\(\dfrac{a^b-a^b\bmodc}{c}=kc+a......
  • 2024暑假总结1
    总结记得26号来见到了一些新同学,还考了一场试,题目都很基础,但是我清楚地记得我把图论大部分内容都忘了,第二题计数题也没调出来。这次经历让我深深感受到半年不碰OI再次上手时原来如此乏力,脑海中搜索出的知识结构图也只有空荡荡的框架而无多少内容。看到一个知识只是粗略知道它的原......
  • 暑假第二周周报
    这一周在主攻搜索,题单里的搜索题感觉写的还是比较顺利的,还剩一题八数码要用到一些进阶的搜索算法。以下是感觉比较有收获的题目:一、数独挑战:用到了位运算,用位运算进行标记以及判定,可以有效节约空间和时间。二、[NOIP2014]寻找道路题目要求在有向图上找一条最短路,满足路径上......
  • 『模拟赛』暑假集训CSP提高模拟4
    Rank一次比一次烂了,鉴定为不写模拟赛记录导致的。A.WhiteandBlack原题ARC148C被自己误导了,导致菊花和链的部分分没拿到。经验++对于每个点的父节点若有$1\lef_i\lti$,则该图构成的菊花图根可能为\(1\)或\(2\),链则不确定首尾。Subtask越来越不好判了www思......
  • 暑假集训csp提高模拟2
    赛时rank11,T130,T20,T320,T420T1活动投票摩尔投票模板题点此查看代码#include<bits/stdc++.h>#include<bits/extc++.h>//usingnamespace__gnu_pbds;//usingnamespace__gnu_cxx;usingnamespacestd;#defineinfile(x)freopen(x,"r",stdin)#define......
  • 暑假集训CSP提高模拟4
    暑假集训CSP提高模拟4\(T1\)P134.WhiteandBlack\(0pts\)原题:[ARC148C]LightsOutonTree翻转方式:从根节点进行\(DFS\),若遇到黑点就进行翻转。最后一定能使全树均为白点,即不存在无解的情况。进而有每个点仅会被主动翻转一次,且翻转顺序与最终结果无关。观察到......
  • 暑假训练第二周周报
    总体学习情况这周时间大多花在写上周的堆栈的题单了,然后比赛又碰到了一些新的知识点,比如无权二分图的最大匹配,01背包的相似例题,但是感觉数据结构的基础还是得练,遇到一些题还是没办法写对。知识点模块1.无权二分图最大匹配用通俗的话来讲,假如有几个男的和几个女的存在暧昧关系,......
  • 暑假集训CSP提高模拟4
    A.WhiteandBlack暴力的\(O(nq)\)做法比较显然,因为对于根节点来说,只有它自己可以改变自己的颜色,因此如果它是黑色则一定需要更改自己,同时把更改传下去(应该没有那种每次真的更改所有节点然后写\(O(nqn^{n})\)的吧),同理,假如根节点更改结束,次级节点就同理了,这是一个连锁的反应,因......