zoukankan      html  css  js  c++  java
  • [PA2010]Riddle

    传送门

    2-sat前缀优化建图,看来上一道题也可以算是前缀优化

    每个点先拆成两个点,表示这个点选或不选,再额外拆成两个点,表示所在部分这个点以及以前的点有/没有选到的

    一条边两边只能选一个点的限制很好处理

    每个部分选一个点的情况大力连边会(n^2)

    这里就要用到我们之前的前缀部分节点,具体含义代码里说

    #include<bits/stdc++.h>
    using namespace std;
    namespace red{
    #define ls(p) (p<<1)
    #define rs(p) (p<<1|1)
    #define mid ((l+r)>>1)
    #define y1 qwq 
    	inline int read()
    	{
    		int x=0;char ch,f=1;
    		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    		if(ch=='-') f=0,ch=getchar();
    		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    		return f?x:-x;
    	}
    	const int N=4e6+10,inf=1<<30;
    	int n,m,k;
    	int head[N],cnt;
    	struct point
    	{
    		int nxt,to;
    		point(){}
    		point(const int &nxt,const int &to):nxt(nxt),to(to){}
    	}a[N<<1];
    	inline void link(int x,int y)
    	{
    		a[++cnt]=(point){head[x],y};head[x]=cnt;
    	}
    	int dfn[N],low[N],st[N],col[N];
    	int idx,top,sum;
    	inline void tarjan(int now)
    	{
    		dfn[now]=low[now]=++idx;
    		st[++top]=now;
    		for(int i=head[now];i;i=a[i].nxt)
    		{
    			int t=a[i].to;
    			if(!dfn[t])
    			{
    				tarjan(t);
    				low[now]=min(low[now],low[t]);
    			}
    			else if(!col[t]) low[now]=min(low[now],dfn[t]);
    		}
    		if(dfn[now]==low[now])
    		{
    			col[now]=++sum;
    			while(st[top]!=now) col[st[top--]]=sum;
    			--top;
    		}
    	}
    	inline void main()
    	{
    		n=read(),m=read(),k=read();
    		for(int x,y,i=1;i<=m;++i)
    		{
    			x=read(),y=read();
    			link(x+n,y),link(y+n,x);
    		}
    		for(int x,y,i=1;i<=k;++i)
    		{
    			int sum=read();y=0;
    			for(int j=1;j<=sum;++j)
    			{
    				x=read();
    				//x+2*n 有过 x+3*n 没有 
    				//x 选了 x+n 不选 
    				if(y)
    				{
    					link(y+n*2,x+n*2);
    					//上一个点有过了,那么到这个点肯定也有了 
    					link(x+3*n,y+3*n);
    					//这个点还没有,上一个点肯定也还没有 
    					link(x,y+3*n);
    					//这个点选了,上一个点以及之前肯定没有 
    					link(y+2*n,x+n);
    					//上一个点有过了,这个点不能选 
    				}
    				link(x,x+2*n);
    				//这个点选了,这个点以及之前有过了 
    				link(3*n+x,x+n);
    				//这个点以及之前没有,这个点不选 
    				y=x;
    			}
    		}
    		for(int i=1;i<=4*n;++i)
    			if(!dfn[i]) tarjan(i);
    		for(int i=1;i<=n;++i)
    		{
    			if(col[i]==col[i+n]||col[i+2*n]==col[i+3*n])
    			{
    				puts("NIE");
    				return;
    			}
    		}
    		puts("TAK");
    	}
    }
    signed main()
    {
    	//freopen("haha.in","r",stdin);
    	red::main();
    	return 0;
    }
    
  • 相关阅读:
    day3
    day2
    day1-存储
    day5-iptables
    MySQL之补充
    11.18
    11.17
    junit基础学习之-测试controller层(2)
    junit基础学习之-简介(1)
    外键和级联
  • 原文地址:https://www.cnblogs.com/knife-rose/p/12701778.html
Copyright © 2011-2022 走看看