6802. NOIP2020.9.19模拟eugene

63 篇文章 0 订阅
19 篇文章 0 订阅

Description

蟹老板经营着能烹饪出世上最好吃蟹黄堡的餐馆——蟹堡王。每天都有大批咸鱼顾客来到他的店子
里品尝蟹黄堡。
作为餐馆的老板,蟹老板要在餐馆的雇员里评出最佳员工,而他的统计方式也非常简单:他先准备
好很多份调查问卷,问卷的形式为一个三元组 (a,b,c),表示询问员工 a,b 中谁更出色。更出色的一名将
会获得 c 分的得分,另一名获得 −c 分的得分。他将这些问卷分发给新老顾客,并由顾客的反馈累加每
名员工的得分。
并且,蟹老板还做出了如下的三点保证:
• 对于每个员工,与它相关的问卷的 c 的和都是个奇数。
• 所有的 c 不是 1 就是 2。
• 每个问卷的 a,b 不相等。
蟹老板想知道是否存在一种情况,使得最终每名员工的得分的绝对值都不超过 1。如果不存在,请输
出 −1,反之,输出每一个问卷对应的答案。

Input

输入文件名为 eugene.in。
输入文件包含 m + 1 行。
第一行两个整数 n,m,表示问卷的数量。蟹堡王的员工编号为 0 到 n − 1。
接下来 m 行,每行三个整数 a i ,b i ,c i ,其中 a i ,b i 表示两名员工的编号,c i 表示问卷的分数。

Output

输出文件名为 eugene.out。
若你能给出合法方案,则输出一个长度为 m 的 0/1 字符串。其中第 i 个字符表示第 i 名顾客的意见
(为 0 表示在你给出的方案中,顾客认为 a i 更为优秀,应该得到正的分数;为 1 则表示顾客认为 b i 更为
优秀)。若有多组合法方案则输出其中任意一组即可。
不存在这样的方案则输出一个整数 −1。

Sample Input

4 8
0 1 1
0 2 1
0 3 1
1 2 1
1 3 1
2 3 1
0 1 2
2 3 2

Sample Output

01001011

Data Constraint

Hint

对于第一个样例:
对于样例给出的方案,四人的最终得分分别为 −1,1,−1,1,所以该方案合法。
明显的,仍有其他方案满足条件(如 10110100 等),所以只需输出其中任意一种即可。

Solution

注意:由于是本人自己想的,所以本方法可能比较暴力,比较劣,但是勉强可以通过这道题。欢迎提供更优的方法的题解。

显然肯定有解。

首先将1和2分开处理,对于同类的边,可以找出一条路径来,将这条路径的每条边的前一个点加上分数,后一个点减去分数。这样就相当于将这条路径的开头的点加上相应分数,在结尾减去。

那么就将这一整条路径合并成一条由路径头指向路径尾的边。

这样对于一个点,同类的路径个数最多为1。因为如果为2那就可以将它们合并了。

最后整张图就变成了若干个环和链。其中每两条边相邻的边都是不同类别的。那么就可以按照相同的办法前一个加,后一个减即可。

对于具体操作,我是先将同类的边分开处理。每次加入读入的一条边,如果当前边的顶点有度数,那么就将与其相连的边打上标记表示它不存在,再加入一条新的边去覆盖这2条或3条边,再将它打上存在标记,并记录这条新边覆盖了哪些边(这里记录编号)。之后维护每个点的度数(即0或1),就能判断是否右边与之相连了。

接下来就将链和环dfs,对每条边规定加分的点。

之后再将每条边覆盖了哪些边分别去dg,每次将这些边的加分的点同样更新一下,最终就可以更新到所有读入的边。

判断一下加分的点在x还是y就能输出答案了。注意输出会很大,要优化一下。

总的来说,思路很简单,代码实现很复杂。

Code

#include<cstring>
#include<cstdio>
#include<algorithm>
#define I int
#define X x[i]
#define Y y[i]
#define a(v) a[tot>>1][v]
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 1000010
using namespace std;
I n,m,x[N],y[N],z[N],ok[N<<2],ans[N<<2],d[N],tot=1,bz[N<<2],bx,by,tx,ty;
I t[N<<2],nx[N<<2],id[N<<2],h[N],w[N<<2],a[N<<2][4],vis[N<<2];
struct edge{I x,y;}E[N<<2];
char ch;
void R(I &x){
	x=0;ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
void add(I x,I y,I z,I o){d[t[++tot]=y]=1,nx[tot]=h[x],h[x]=tot,id[tot]=z,w[tot]=o;E[tot>>1]=edge{x,y};}
void work(I p){
	memset(d,0,sizeof d);
	F(i,1,m) if(z[i]==p){
		bx=h[X]>>1,by=h[Y]>>1,tx=t[h[X]],ty=t[h[Y]];
		if(!d[X]&&!d[Y]){add(X,Y,i,p),add(Y,X,i,p);bz[tot>>1]=1;}
		else if(d[X]&&d[Y]){
			add(X,Y,i,p),add(Y,X,i,p);
			add(tx,ty,0,p),add(ty,tx,0,p);bz[tot>>1]=1;
			a(0)=3,bz[a(1)=bx]=bz[a(2)=(tot>>1)-1]=bz[a(3)=by]=d[X]=d[Y]=0;
			if((X==tx&&Y==ty)||(X==ty&&Y==tx)){
				a(0)=2;
				ans[tot>>1]=X;
				bz[tot>>1]=0;	
			}
		}
		else if(d[X]&&!d[Y]){
			add(X,Y,i,p),add(Y,X,i,p);
			add(tx,Y,0,p),add(Y,tx,0,p);bz[tot>>1]=1;
			a(0)=2,bz[a(1)=bx]=bz[a(2)=(tot>>1)-1]=d[X]=0;
		}
		else if(!d[X]&&d[Y]){
			add(X,Y,i,p),add(Y,X,i,p);
			add(X,ty,0,p),add(ty,X,0,p);bz[tot>>1]=1;
			a(0)=2,bz[a(1)=(tot>>1)-1]=bz[a(2)=by]=d[Y]=0;
		}
	}
}
I pd(I x){
	I cnt=0;
	for(I k=h[x];k;k=nx[k]){
		cnt+=bz[k>>1];
		if(cnt>1) return 0;
	}
	return cnt==1;
}
void dg(I x){
	vis[x]=1;
	for(I k=h[x];k;k=nx[k]) if(bz[k>>1]){
		bz[k>>1]=0;
		ans[k>>1]=x;
		dg(t[k]);
		return;
	}
}
void get(I x){
	vis[x]=1;
	tx=ans[x];
	if(tx==E[a[x][1]].x||tx==E[a[x][1]].y){
		for(I k=1;k<=a[x][0];k++){
			ans[a[x][k]]=tx;
			tx^=E[a[x][k]].x^E[a[x][k]].y;
		}
	}
	else{
		for(I k=a[x][0];k>=1;k--){
			ans[a[x][k]]=tx;
			tx^=E[a[x][k]].x^E[a[x][k]].y;
		}
	}
	for(I k=1;k<=a[x][0];k++) get(a[x][k]);
}
I main(){
	freopen("eugene.in","r",stdin);
	freopen("eugene.out","w",stdout);
	R(n),R(m);
	F(i,1,m){R(X),R(Y),R(z[i]);X++,Y++;}
	tot=1;
	work(1),work(2);
	F(i,1,n) if(pd(i)&&!vis[i]) dg(i);
	F(i,1,n) if(!vis[i]) dg(i);
	mem(vis,0);
	F(i,1,tot>>1) if(ans[i]&&!vis[i]) get(i);
	F(i,1,tot>>1) if(id[i*2]) ok[id[i*2]]=(ans[i]==y[id[i*2]]);
//	printf("%d -> %d:(%d)  type :%d\n",E[i].x,E[i].y,ans[i],z[id[i*2]]);
	F(i,1,m) putchar(48+ok[i]);
	//对于每一条边将两边有度数的点缩成一条边
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值