Luogu P6185 【[NOI Online 提高组]序列】

本来考场切了的,结果代码没保存,爆炸成80,自闭了。
考场上我是一个一个部分分做的,所以这里我也一个一个部分分写。

0pts \(n=1\)

这个档位是针对样例写的。。。因为不写我就过不了样例。。。
显然如果有一个1操作就输出YES,没有就看相不相等完事。。。
贴一个没有什么用的代码。

inline void Solve_0pts()
{
	if(flg1==1) 
	{
		printf("YES\n");
	}
	else 
		if(a[1]==b[1])
		{
			printf("YES\n");
		}
		else
		{
			printf("NO\n");
		}
		return ;
}

60pts \(n=2\)

分类讨论:

1.如果只有1操作,那很显然,只要\(a1-b1=a2-b2\)就完事了。
2.如果只有2操作,那也很显然,只要\(a1-b1==b2-a2\)就完事了。
3.如果1,2操作都有,那么假设\(a1+k1+k2=b1\),\(a2+k1-k2=b2\),合并一下就可以得到\(a1+a2-b1-b2=-2*k2\),所以只要作差然后判定一下是不是偶数就可以了。
贴一个有点用的代码。

inline void Solve_60pts()
{
	if(flg1)
	{
		if(a[1]-b[1]==a[2]-b[2])
		{
			printf("YES\n");
			return;
		}
	}
	if(flg2)
	{
		if(a[1]-b[1]==b[2]-a[2])
		{
			printf("YES\n");
			return;
		}
	}
	if(flg1&&flg2)
	{
		if((a[1]+a[2]-b[1]-b[2])%2==0)
		{
			printf("YES\n");
			return;
		}
	}
	printf("NO\n");
}

80pts \(ti=2\)

发现被链接的两个点的值可以互相流动,于是把链接的点在a和b数列里分别缩点,这里用并查集实现。由于同一个集合里的点的值可以互相流动,所以这里只要a数列和b数列每个对应集合里的点权和相同,那么就一定是合法的,否则不合法,代码也比较好写。

inline int find(int x) 
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}

inline void merge(int x,int y)
{
	int fx=find(x);
	int fy=find(y);
	va[fy]+=va[fx];
	va[fx]=0;fa[fx]=fy;
}

inline void Solve_80pts()
{
	for(R int i=1;i<=n*2;i++) fa[i]=i;
	for(R int i=1;i<=n;i++) va[i]=a[i];
	for(R int i=1;i<=n;i++) va[n+i]=b[i];
	for(R int i=1;i<=m;i++)
	{
		int x=u[i],y=v[i];
		if(find(x)==find(y)) continue;
		merge(x,y);
		merge(x+n,y+n);
	}
	for(R int i=1;i<=n;i++)
		if(find(i)==i)
		{
			if(va[i]!=va[i+n])
			{
				printf("NO\n");
				return;
			}
		}
	printf("YES\n");
}

100pts

为了方便,先用一个sm数组存下a与b对应位置的差值。
发现80分缩点的做法十分舒适,所以先把操作2缩点,然后就只剩下操作1了。由于操作2已经缩点,操作1再缩点感觉不太现实,于是考虑连边(好玄学)。 把1操作的两个点连边之后(注意是连缩点之后的)。然后继续开始手玩样例,发现假如有两个点\((x,y)\),使得这两个点之间有一条长度为偶数的路径,那这两个点之间也是可以相互交流权值的。更特别一点的是,如果一个联通块里面有一个长度为奇数的环,那任意两个点之间都会有一条长度为偶数的路径。为什么呢,因为环上的点显然是有的,环外的点如果到一个点不经过环长度为奇数,那就去跑一边环就变成偶数了,所以这个命题成立。然后就可以把有奇环的联通块看成一个点了。那么这样的一个块,
如果差值的和或者差是偶数,就是可以的,否则就不行。注意如果一个点连成了自环,那么也是可以的算成这一种块。如果这个块恰好是一个二分图,那么黑白染色之后,权值就只可以在黑点集合中的点之间和白点集合中的点之间各自流动,因为同一个集合中的任意两个点的距离必定是偶数。所以就计算黑点差值之和与白点的差值之和是否相同就可以了。我的代码中没有统计和,是直接一边染色一边相减的。对于单独的点,我们也把它看成一个二分图来处理即可。部分代码就懒得丢了,就是完整代码里100pts的部分(突然反应过来,那我上面丢代码干啥???)

完整代码

#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

#define R register
#define ll long long
const int MAXN=2e5+10;

int n,m;
int a[MAXN],b[MAXN];
int opt[MAXN],u[MAXN],v[MAXN];

int flg1=0,flg2=0;

inline void Init()
{
	scanf("%d%d",&n,&m);
	for(R int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(R int i=1;i<=n;i++)	scanf("%d",&b[i]);
	flg1=flg2=0;
	for(R int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&opt[i],&u[i],&v[i]);
		if(opt[i]==1) flg1=1;
		if(opt[i]==2) flg2=1;
	}

}

inline void Solve_0pts()
{
	if(flg1==1) 
	{
		printf("YES\n");
	}
	else 
		if(a[1]==b[1])
		{
			printf("YES\n");
		}
		else
		{
			printf("NO\n");
		}
		return ;
}

inline void Solve_60pts()
{
	if(flg1)
	{
		if(a[1]-b[1]==a[2]-b[2])
		{
			printf("YES\n");
			return;
		}
	}
	if(flg2)
	{
		if(a[1]-b[1]==b[2]-a[2])
		{
			printf("YES\n");
			return;
		}
	}
	if(flg1&&flg2)
	{
		if((a[1]+a[2]-b[1]-b[2])%2==0)
		{
			printf("YES\n");
			return;
		}
	}
	printf("NO\n");
}

int fa[MAXN];
ll va[MAXN];

inline int find(int x) 
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}

inline void merge(int x,int y)
{
	int fx=find(x);
	int fy=find(y);
	va[fy]+=va[fx];
	va[fx]=0;fa[fx]=fy;
}

inline void Solve_80pts()
{
	for(R int i=1;i<=n*2;i++) fa[i]=i;
	for(R int i=1;i<=n;i++) va[i]=a[i];
	for(R int i=1;i<=n;i++) va[n+i]=b[i];
	for(R int i=1;i<=m;i++)
	{
		int x=u[i],y=v[i];
		if(find(x)==find(y)) continue;
		merge(x,y);
		merge(x+n,y+n);
	}
	for(R int i=1;i<=n;i++)
		if(find(i)==i)
		{
			if(va[i]!=va[i+n])
			{
				printf("NO\n");
				return;
			}
		}
	printf("YES\n");
}

int To[MAXN+MAXN],Next[MAXN+MAXN],Head[MAXN],cnt;

inline void add(int x,int y)
{
	cnt++;
	To[cnt]=y;
	Next[cnt]=Head[x];
	Head[x]=cnt;
}

int sm[MAXN];
int co[MAXN];

int sum,tag;

inline void dfs(int x,int col)
{
	co[x]=col;
	if(col) sum+=sm[x];
	else sum-=sm[x];
	for(R int i=Head[x];i;i=Next[i])
	{
		int y=To[i];
		if(co[y]==-1)
			dfs(y,col^1);
		else
			if(co[x]==co[y]) tag=1;
	}
}

inline void Solve_100pts()
{
	memset(Head,0,sizeof(Head));
	memset(co,-1,sizeof(co));
	cnt=0;
	for(R int i=1;i<=n;i++)
		sm[i]=a[i]-b[i];
	for(R int i=1;i<=n;i++)
		fa[i]=i;
	for(R int i=1;i<=m;i++)
	{
		if(opt[i]==1) continue;
		int x=u[i],y=v[i];
		if(find(x)==find(y)) continue;
		int fx=find(x),fy=find(y);
		fa[fx]=fy;sm[fy]+=sm[fx];sm[fx]=0;
	}
	for(R int i=1;i<=m;i++)
	{
		if(opt[i]==2) continue;
		int x=u[i],y=v[i];
		x=find(x);y=find(y);
		add(x,y);add(y,x);
	}
	bool flg=1;
	for(R int i=1;i<=n;i++)
		if(find(i)==i&&co[i]==-1)
		{
			sum=tag=0;
			dfs(i,0);
			for(R int j=Head[i];j;j=Next[j])
				if(To[j]==i) tag=1;
			if(tag==1)
			{
				if(sum%2!=0) flg=0;
			}
			else
			{
				if(sum!=0) flg=0;
			}

		}
	if(flg) printf("YES\n");
	else printf("NO\n");
}

int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	int test;scanf("%d",&test);
	while(test--)
	{
		Init();
		if(n==1) {Solve_0pts(); continue;}
		if(n<=2) {Solve_60pts();continue;}
		if(flg1==0&&flg2==1) {Solve_80pts();continue;}
		Solve_100pts();
	}
	return 0;
}

然而这并不能改变我总分100的事实,自闭了。。。

posted @ 2020-04-30 17:37  HN-wrp  阅读(309)  评论(0编辑  收藏  举报