题解 P6378 【[PA2010]Riddle】
首先不难看出对于本题的点与点之间的限制关系,我们可以考虑用
接下来的处理中,
对于每条边至少有一个端点是关键点这一限制,若边两端为
处理使得每个部分恰有一个关键点这一限制时,发现要进行像一个点向该部分中除它以外的所有点连边的操作,这样的话,边数会达到
对于每个点,我们这里在其所在的部分中考虑,每个点用
连边时我们可以进行以下的操作:
通过这样的前缀优化建图,就不用再连那么多边了,同时题目的限制条件也都可以满足。
具体实现细节看代码吧。
#include<bits/stdc++.h>
#define p1(x) x
#define p0(x) x+n
#define pre1(x) x+2*n
#define pre0(x) x+3*n
#define maxn 8000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,k,dfn_cnt,co_cnt,top;
int dfn[maxn],low[maxn],co[maxn],st[maxn],a[maxn];
bool vis[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
void tarjan(int x)
{
dfn[x]=low[x]=++dfn_cnt;
st[++top]=x,vis[x]=true;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
co_cnt++;
int now;
do
{
now=st[top--];
vis[now]=false;
co[now]=co_cnt;
}while(now!=x);
}
}
bool check()
{
for(int i=1;i<=4*n;++i)
if(!dfn[i])
tarjan(i);
for(int i=1;i<=n;++i)
if(co[p1(i)]==co[p0(i)]||co[pre1(i)]==co[pre0(i)])
return false;
return true;
}
int main()
{
read(n),read(m),read(k);
for(int i=1;i<=m;++i)
{
int x,y;
read(x),read(y);
add(p0(x),p1(y)),add(p0(y),p1(x));
}
for(int i=1;i<=k;++i)
{
int w;
read(w);
for(int j=1;j<=w;++j)
read(a[j]),add(p1(a[j]),pre1(a[j])),add(pre0(a[j]),p0(a[j]));
for(int j=2;j<=w;++j)
{
add(pre1(a[j-1]),pre1(a[j])),add(pre0(a[j]),pre0(a[j-1]));
add(pre1(a[j-1]),p0(a[j])),add(p1(a[j]),pre0(a[j-1]));
}
}
if(check()) puts("TAK");
else puts("NIE");
return 0;
}