bzoj 2199: [Usaco2011 Jan]奶牛议会

Description

由于对Farmer John的领导感到极其不悦,奶牛们退出了农场,组建了奶牛议会。议会以“每头牛 都可以获得自己想要的”为原则,建立了下面的投票系统: M只到场的奶牛 (1 <= M <= 4000) 会给N个议案投票(1 <= N <= 1,000) 。每只 奶牛会对恰好两个议案 B_i and C_i (1 <= B_i <= N; 1 <= C_i <= N)投 出“是”或“否”(输入文件中的'Y'和'N')。他们的投票结果分别为VB_i (VB_i in {'Y', 'N'}) and VC_i (VC_i in {'Y', 'N'})。 最后,议案会以如下的方式决定:每只奶牛投出的两票中至少有一票和最终结果相符合。 例如Bessie给议案1投了赞成'Y',给议案2投了反对'N',那么在任何合法的议案通过 方案中,必须满足议案1必须是'Y'或者议案2必须是'N'(或者同时满足)。 给出每只奶牛的投票,你的工作是确定哪些议案可以通过,哪些不能。如果不存在这样一个方案, 输出"IMPOSSIBLE"。如果至少有一个解,输出: Y 如果在每个解中,这个议案都必须通过 N 如果在每个解中,这个议案都必须驳回 ? 如果有的解这个议案可以通过,有的解中这个议案会被驳回 考虑如下的投票集合: - - - - - 议案 - - - - - 1 2 3 奶牛 1 YES NO 奶牛 2 NO NO 奶牛 3 YES YES 奶牛 4 YES YES 下面是两个可能的解: * 议案 1 通过(满足奶牛1,3,4) * 议案 2 驳回(满足奶牛2) * 议案 3 可以通过也可以驳回(这就是有两个解的原因) 事实上,上面的问题也只有两个解。所以,输出的答案如下: YN?

Input

* 第1行:两个空格隔开的整数:N和M * 第2到M+1行:第i+1行描述第i只奶牛的投票方案:B_i, VB_i, C_i, VC_i

Output

* 第1行:一个含有N个字符的串,第i个字符要么是'Y'(第i个议案必须通过),或者是'N' (第i个议案必须驳回),或者是'?'。 如果无解,输出"IMPOSSIBLE"。
       上了黑板手推就是思路比较清晰呀。

       关系很一样,也就是“或”的关系,不过问法比较有意思,问的是第i个议案是必须被通过,必须不被通过,可被也可不被,还是不属于任何方案。

       翻译一下题意就是,如果某个可行解包含Y,却没有可行解包含N,就输出Y,以此类推。

       不知道有没有可行解能包含它,我们就拿它构造可行解啊,如果能构造出来可行解就可行,构造不出来就不行,由于连的边是依赖关系,所以我们可以一直走,走到没有依赖关系为止,看有没有一个点的正反被这个可行解同时包含即可。

       下附AC代码。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stack>
#define maxn 3000005
using namespace std;
//0:选,1:不选,2:存在,3:不存在 
int n,m,k,tot,num,cnt;
int head[maxn<<2],to[6*maxn],nex[6*maxn],pre[6*maxn];
void add(int x,int y)
{
	to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int low[maxn<<2],dfn[maxn<<2],bel[maxn<<2];
bool vis[maxn<<2];
stack<int>s;
void tarjan(int now)
{
	low[now]=dfn[now]=++cnt;
	vis[now]=1;
	s.push(now);
	for(int i=head[now];i;i=nex[i])
	{
		if(!dfn[to[i]])
		{
			tarjan(to[i]);
			low[now]=min(low[now],low[to[i]]);
		}
		else if(vis[to[i]])
		{
			low[now]=min(low[now],dfn[to[i]]);
		}
	}
	if(low[now]==dfn[now])
	{
		num++;
		int temp;
		do
		{
			temp=s.top();s.pop();
			bel[temp]=num;
			vis[temp]=0;
		}while(temp!=now);
	}
	return;
}
int main()
{
	memset(pre,-1,sizeof(pre));
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);x--;y--;
		int x1=((x<<2)|1),y1=((y<<2)|1);
		x<<=2;y<<=2;
		add(x1,y);add(y1,x);
	}
	for(int i=1;i<=k;i++)
	{
		int x,last;
		scanf("%d%d",&x,&last);
		last--;
		for(int j=1;j<x;j++)
		{
			int y;
			scanf("%d",&y);y--;
			pre[y]=last;last=y;
		}
	}
	for(int i=0;i<n;i++)
	{
		int x1=(i<<2),x2=(x1|1),x3=(x2+1),x4=(x3+1);
		add(x1,x3);add(x4,x2);
		if(pre[i]!=-1)
		{
			int j=pre[i];
			int y1=(j<<2),y2=(y1|1),y3=(y2+1),y4=(y3+1);
			add(y3,x3);add(x4,y4);add(y3,x2);add(x1,y4);
		}
	} 
	for (int i=0;i<(n<<2);i++)
    if (!dfn[i]) 
	tarjan(i);
    for (int i=0;i<(n<<2);i++)
    if (bel[i]==bel[i^1]) 
	{
		printf("NIE");
		return 0;
	}
	printf("TAK\n");
	return 0;
}


        





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值