zoukankan      html  css  js  c++  java
  • 【BZOJ3495】PA2010 Riddle

    题目大意

    (n)个城镇被分成了(k)个郡,有(m)条连接城镇的无向边。要求给每个郡选择一个城镇作为首都,满足每条边至少有一个端点是首都。

    题目分析

    每条边至少有一个端点是首都,每个郡至多一个首都,很容易想到(2-sat)判定。

    考虑如何建边。我们用(x)表示在编号为(x)的节点建首都,(x')表示不在该点建首都。

    对于一条边的两个端点,若左端点(l)不为首都,则右端点(r)必为首都;右端点同理,因此(l)(r')连边,(r)(l')连边。

    对于同一个郡内的点,若(a_1)为首都,则其他点都不能为首都,按照套路我们应取郡内每一个点(a_i')向郡内其他点(a_j')连边。但这样边数是(n^2)级别的,难以接受。

    于是考虑优化建边。观察同一个郡内的点集({a_1,a_2,...,a_n}),我们发现建边时,有许多重复的边(()如选择(a_3)(a_4)作为首都,那么({a_1,a_2})以及({a_5,a_6})都不能作为首都()),因此我们很容易想到前缀与后缀优化连边。

    我们新增虚节点(a_i''),并且(a_{i+1}'')(a_{i}'')连边,(a_i'')(a_i')连边,那么如果选择(a_i)作为首都,对于(a_j(j<i))就只需要(a_i)(a_{i-1}'')连边即可。这是前缀优化。对于(a_j(j>i))同理用后缀优化即可。

    #include <bits/stdc++.h>
    using namespace std;
    int getint(){
    	int w=0;char ch=getchar();
    	while(ch<'0'||ch>'9')ch=getchar();
    	while(ch>='0'&&ch<='9')w=w*10+ch-'0',ch=getchar();
    	return w;
    }
    const int maxn=4000005;
    int n,m,k,h[maxn],dfn[maxn],low[maxn],st[maxn],bel[maxn],scc,sign,top;
    bool instack[maxn];
    struct edge{int to,next;}e[maxn*5];
    void addedge(int x,int y){
    	static int cnt;
    	e[++cnt]=(edge){y,h[x]};h[x]=cnt;
    }
    struct info{int x,i,y;};
    void dfs(int x){
    	static info S[maxn];
    	static int Top;
    	int i,y;
    call:
    	dfn[x]=low[x]=++sign;st[++top]=x;instack[x]=1;
    	for(i=h[x];i;i=e[i].next){
    		y=e[i].to;
    		if(!dfn[y]){
    			S[++Top]=(info){x,i,y};
    			x=y;goto call;
    Return:;
    			low[x]=min(low[x],low[y]);
    		}
    		else if(instack[y])low[x]=min(low[x],dfn[y]);
    	}
    	if(low[x]==dfn[x]){
    		scc++;
    		for(;;){
    			int y=st[top--];
    			instack[y]=0;bel[y]=scc;
    			if(y==x)break;
    		}
    	}
    	if(Top){x=S[Top].x;i=S[Top].i;y=S[Top].y;Top--;goto Return;}
    }
    int main(){
    //	freopen("capital.in","r",stdin);
    //	freopen("capital.out","w",stdout);
    	n=getint();m=getint();k=getint();
    	for(int i=1;i<=m;i++){
    		int x=getint(),y=getint();
    		addedge(x+n,y);addedge(y+n,x);
    	}
    	for(int i=1;i<=k;i++){
    		int x=getint();
    		for(int j=1;j<=x;j++)st[j]=getint();
    		for(int j=x;j>1;j--)addedge(st[j]+2*n,st[j-1]+2*n);
    		for(int j=1;j<x;j++)addedge(st[j]+3*n,st[j+1]+3*n);
    		for(int j=1;j<=x;j++){
    			if(j>1)addedge(st[j],st[j-1]+2*n);
    			if(j<x)addedge(st[j],st[j+1]+3*n);
    		}
    	}
    	for(int i=1;i<=n;i++)addedge(i+2*n,i+n),addedge(i+3*n,i+n);
    	for(int i=1;i<=4*n;i++)if(!dfn[i])dfs(i);
    	bool flag=0;
    	for(int i=1;i<=n;i++)if(bel[i]==bel[i+n])flag=1;
    	if(!flag)puts("TAK");
    	else puts("NIE");
    }
    
  • 相关阅读:
    Windows Server 2016-Active Directory复制概念(二)
    Windows Server 2016-Active Directory复制概念(一)
    Windows Server 2016-Wbadmin命令行备份域控制器
    Windows Server 2016-图形化备份域控制器
    Windows Server 2016-Nano Server介绍
    Windows Server 2016-系统安装软硬件要求
    每天一个linux命令(51)--grep命令
    每天一个linux命令(50)--date命令
    每天一个linux命令(49)--diff命令
    每天一个linux命令(48)--ln命令
  • 原文地址:https://www.cnblogs.com/Trrui/p/9646021.html
Copyright © 2011-2022 走看看