まず、この問題のポイント間の制限関係を確認することは難しくありません。\(2-SAT \)を使用して、状態\(x \)から状態\(y \)への有向エッジを接続することで解決できます。つまり、状態\(x \)が存在する場合、状態\(y \)が存在する必要があります。
次の処理では、\(x \)はポイント\(x \)がキーポイントとして選択されていることを示し、\(x ^ \ prime \)はポイント\(x \)がキーポイントとして選択されていないことを示しています\(x \ longrightarrow y \)は、2つの状態の間に有向エッジがあることを示します。
各エッジの少なくとも1つの端点がキーポイントであるという制限のために、エッジの両端が\(x \)と\(y \)である場合、直接\(x ^ \ prime \ longrightarrow y \、\ y ^ \に進みます。プライム\ longrightarrow x \)。
各パーツに厳密に1つのキーポイントがあるという制限に対処する場合、エッジをパーツ以外のすべてのポイントに接続するような操作が必要であることがわかります。これにより、エッジの数は\(n ^ 2 \)になります。このレベルは許容できないため、構造の最適化を検討する必要があります。
各ポイントについて、それが配置されている部分でそれを考慮し、各ポイントは\(a_i \)で表されます。次に、新しい状態が\(pre_i \)に追加されます。これは、パーツの前にあることを意味します\(i \ )ポイントがキーポイントとして選択されているかどうか。
エッジを接続するときに、次の操作を実行できます。
\(a_i \ longrightarrow pre_ {a_i} \、\ pre_ {a_i} ^ \ prime \ longrightarrow a_i ^ \ prime \)
\(pre_ {a_ {i-1}} \ longrightarrow pre_ {a_i} \、\ pre_ {a_ {i-1}} \ longrightarrow a_i ^ \ prime \)
\(pre_ {a_i} ^ \ prime \ longrightarrow pre_ {a_ {i-1}} ^ \ prime \、\ a_i \ longrightarrow pre_ {a_ {i-1}} ^ \ prime \)
構造を最適化するこのような接頭辞を使用すると、それほど多くの側を接続する必要がなく、問題の制約も満たすことができます。
特定の実装の詳細については、コードを参照してください。
\(コード:\)
#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(pre1(a[j-1]),p0(a[j]));
add(pre0(a[j]),pre0(a[j-1])),add(p1(a[j]),pre0(a[j-1]));
}
}
if(check()) puts("TAK");
else puts("NIE");
return 0;
}