luogu P6830 [IOI2020]连接擎天树
题面传送门
其实IOI题目一出来就口胡出这道题了,然而一直不想打,直到WC之后想缓解一下WC爆炸的心情,所以滚来写这道题。
这道题果然没有让我失望!
我们可以考虑一件事情:只要有\(p{i,j}=3\)或一个联通块中有两个环,那么一定无解。因为这样子一定有至少一个\(p_{i,j}=4\)
这样就少掉了一种情况。
之后我们可以考虑以\(p{i,j}=0\)为界,将整个图割成若干个联通块。
之后因为只有一个环,所以先缩点掉所有\(p{i,j}=1\)的情况,然后把剩下的扔到环上判是否无解即可。
注意二元环也是无解。
代码实现(注释中是调试的交互库):
#include<cstdio>
#include<vector>
using namespace std;
int n,a[1039][1039],f[1039],un,wn,fa[1039],last[1039],flag[1039],vis[1039];
//flag表示当前点是否为单点,vis表示当前所在的联通块是不是树
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void merge(int x,int y){
un=find(x);wn=find(y);
if(un==wn) return;f[un]=wn;
}
void build(std::vector<std::vector<int> > b);
int construct(std::vector<std::vector<int> > p){
register int i,j;n=p.size();
for(i=1;i<=n;i++){
for(j=1;j<=n;j++) a[i][j]=p[i-1][j-1],p[i-1][j-1]=0;
}
for(i=1;i<=n;i++){
for(j=1;j<=n;j++) if(a[i][j]==3) return 0;
}
for(i=1;i<=n;i++) f[i]=i;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++) if(a[i][j]==1) merge(i,j);
}
for(i=1;i<=n;i++){
for(j=1;j<=n;j++)if(find(i)==find(j)&&a[i][j]^1) return 0;
}
for(i=1;i<=n;i++) if(find(i)!=i) flag[i]=1,flag[find(i)]=1;
for(i=1;i<=n;i++){
if(flag[i]&&find(i)!=i) p[i-1][find(i)-1]=p[find(i)-1][i-1]=1;
}
for(i=1;i<=n;i++) fa[i]=find(i);
for(i=1;i<=n;i++) f[i]=i;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++) if(a[i][j]==2)merge(fa[i],fa[j]);
}
for(i=1;i<=n;i++) if(find(i)!=i) vis[i]=1,vis[find(i)]=1;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(fa[i]!=fa[j]&&find(fa[i])==find(fa[j])&&a[i][j]^2) return 0;
}
}
for(i=1;i<=n;i++) if(vis[i]&&find(i)==i) last[i]=i;
for(i=1;i<=n;i++) if(vis[i]&&find(i)!=i) p[last[find(i)]-1][i-1]=p[i-1][last[find(i)]-1]=1,last[find(i)]=i;
for(i=1;i<=n;i++){
if(vis[i]&&find(i)==i){
if(p[last[i]-1][i-1]) return 0;
p[last[i]-1][i-1]=p[i-1][last[i]-1]=1;
}
}
return build(p),1;
}
/*void build(std::vector<std::vector<int> > b){
register int i,j;int n=b.size();
for(i=0;i<n;i++){
for(j=0;j<n;j++) printf("%d ",b[i][j]);printf("\n");
}
}
int main(){
freopen("1.in","r",stdin);
vector<vector<int> > a;
vector<int> b;
a.clear();int n,x;
register int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++){
b.clear();
for(j=1;j<=n;j++) scanf("%d",&x),b.push_back(x);
a.push_back(b);
}
printf("%d\n",construct(a));
}*/