bzoj 3495 PA2010 Riddle

http://www.elijahqi.win/archives/3465
Description
有n个城镇被分成了k个郡,有m条连接城镇的无向边。
要求给每个郡选择一个城镇作为首都,满足每条边至少有一个端点是首都。

Input
第一行有三个整数,城镇数n(1<=n<=10^6),边数m(0<=m<=10^6),郡数k(1<=k<=n)。
接下来m行,每行有两个整数ai和bi(ai≠bi),表示有一条无向边连接城镇ai和bi。
接下来k行,第j行以一个整数wj开头,后面是wj个整数,表示第j个郡包含的城镇。
Output
若有解输出TAK,否则输出NIE。

Sample Input
6 5 2
1 2
3 1
1 4
5 2
6 2
3 3 4 2
3 1 6 5
Sample Output
TAK
HINT

Source
鸣谢onion_cyc提供翻译

感觉自己太菜了为了这点东西调试一下午

考虑暴力n^2建边很好想 那GG做不了怎么办 优化建图即

设s[i]表示在当前这个郡里 我现在前i个中是否存在首都s[i][0]不存在s[i][1]存在

然后那么有连边方式设u’为u的反面点其他字母同理

有关联的两边u->v’ v->u’

x->pre[i-1]’ pre[i-1]->x’

x->pre[i] pre[i]’->x’

pre[i-1]->pre[i] pre[i]’->pre[i-1]’

其实这样也是有情况会有问题的 不符合我们建图的初衷但是i只要求输出TAKNIE 所以因为题目的特殊限制即 一条边两边至少有一个是首都 o所以这样跑出来是对的

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=4e6+10;
struct node{
    int y,next;
}data[N<<1];
bool stackf[N];
int h[N],cnt,num,q[N],id[N>>2],s,b[N],low[N],dfn[N],top,m,k,n;
inline void insert1(int x,int y){
    data[++num].y=y;data[num].next=h[x];h[x]=num;
}
inline void tarjan(int x){
    dfn[x]=low[x]=++num;stackf[x]=1;q[++top]=x;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;
        if (!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);else if(stackf[y]) low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        int y;++s;
        do{
            y=q[top--];stackf[y]=0;b[y]=s;
        }while(y!=x);
    }
}
int main(){
    //freopen("bzoj3495.in","r",stdin);
    n=read();m=read();k=read();
    for (int i=1;i<=n;++i) cnt+=2,id[i]=cnt;
    for (int i=1;i<=m;++i){
        int x=read(),y=read();
        insert1(id[x]^1,id[y]);insert1(id[y]^1,id[x]);
    }
    for (int owo=1;owo<=k;++owo){
        int t=read(),x=read(),lst=(cnt+=2);
        insert1(id[x],cnt);insert1(cnt^1,id[x]^1);
        for (int i=2;i<=t;++i){
            x=read();cnt+=2;
            insert1(id[x],cnt);insert1(cnt^1,id[x]^1);
            insert1(id[x],lst^1);insert1(lst,id[x]^1);
            insert1(lst,cnt);insert1(cnt^1,lst^1);lst=cnt;
        }
    }num=0;bool flag=0;
    for(int i=2;i<=cnt;++i) if (!dfn[i]) tarjan(i);
    for (int i=1;i<=n;++i)
        if (b[id[i]]==b[id[i]^1]) {flag=1;break;}
    if (flag) puts("NIE");else puts("TAK");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值