BZOJ 3495: PA2010 Riddle

Description

\(n\)个城市,\(k\)个国家,\(m\)条边,边两边至少有一个首都,问是否存在合法解。\(1\leqslant k\leqslant n,m\leqslant 10^6\)

Solution

2-SAT.

有几个限制条件一起列上...

这样建图是\(O(n^2)\)的...用前缀和表示来限制一下首都个数...

1.一个点不是首都,那么另一个点一定是首都.

2.前缀和为1,这个点一定为0.

3.这个点为1,前缀和一定为1.

4.这个点为1,前一个点一定为0.

5.这个位置的前缀和为0,前一个位置的前缀和一定为0.

6.这个位置的前缀和为1,后一个位置的前缀和一定为1.

然后缩点判断一下...用vector被卡常了...

Code

/**************************************************************
    Problem: 3495
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:20436 ms
    Memory:350776 kb
****************************************************************/
 
#include <bits/stdc++.h>
using namespace std;
 
const int N = 4000050;
const int M = 20000050;
 
inline int in(int x=0,char c=getchar()) { while(c>'9'||c<'0') c=getchar();
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x; }
 
int n,k,m,cp,bp,tp;
int a[N];
int b[N],bl[N],dfn[N],low[N],stk[N];
 
int ep;
int nxt[M],tto[M],hd[M];
 
void AddEdge(int fr,int to) { nxt[++ep]=hd[fr],tto[ep]=to,hd[fr]=ep; }
void Tarjan(int u,int fa) {
    dfn[u]=low[u]=++cp,b[u]=1,stk[++tp]=u;
    for(int i=hd[u],v;i;i=nxt[i]) {
        v=tto[i];
        if(!dfn[v]) Tarjan(v,u),low[u]=min(low[u],low[v]);
        else if(b[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]) {
        for(bp++;;) {
            int v=stk[tp];
            bl[v]=bp,b[v]=0,tp--;
            if(u==v) break;
        }
    }
}
int main() {
    n=in(),m=in(),k=in();
    for(int i=1;i<=m;i++) {
        int u=in(),v=in();
        AddEdge(u,v+n);
        AddEdge(v,u+n);
    }
    for(int i=1,t,j;i<=k;i++) {
        for(t=in(),j=1;j<=t;j++) a[j]=in();
        for(j=1;j<=t;j++) {
            if(j+1<=t)
                AddEdge(a[j+1]+2*n,a[j]+2*n),AddEdge(a[j]+3*n,a[j+1]+3*n),
                AddEdge(a[j]+3*n,a[j+1]),AddEdge(a[j+1]+n,a[j]+2*n);
            AddEdge(a[j]+n,a[j]+3*n),AddEdge(a[j]+2*n,a[j]);
        }
    }
    for(int i=1;i<=4*n;i++) if(!dfn[i]) Tarjan(i,i);
 
/*  for(int i=1;i<=4*n;i++) {
        cout<<i<<" --> ";
        for(int j=0;j<(int)g[i].size();j++) cout<<g[i][j]<<" ";cout<<endl;
    }
    for(int i=1;i<=4*n;i++) cout<<dfn[i]<<" ";cout<<endl;
    for(int i=1;i<=4*n;i++) cout<<low[i]<<" ";cout<<endl;
    for(int i=1;i<=4*n;i++) cout<<bl[i]<<" ";cout<<endl;*/
    for(int i=1;i<=n;i++) if(bl[i]==bl[i+n]) return puts("NIE"),0;
    for(int i=2*n+1;i<=3*n;i++) if(bl[i]==bl[i+n]) return puts("NIE"),0;
    return puts("TAK"),0;
}

  

转载于:https://www.cnblogs.com/beiyuoi/p/6741013.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值