BZOJ3440 传球游戏

来源:互联网 发布:蜂窝移动网络数据漫游 编辑:程序博客网 时间:2024/06/17 11:58

我们可以考虑拆点,把每个点拆成从左边过来和从右边过来两个,这样2n个点形成了一个环套树森林,一个人满足无论如何只能经过一次当且仅当他拆出来的两个点x和y满足x不是y的祖先,y不是x的祖先且x,y都不在环上

dfs一下即可

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<vector>#include<map>#include<set>#include<bitset>#include<queue>#include<stack>using namespace std;#define MAXN 1000010#define MAXM 1010#define INF 1000000000#define MOD 1000000007#define eps 1e-8#define ll long longstruct vec{int to;int fro;};vec mp[MAXN];int tai[MAXN],cnt;int n;int t[MAXN];int vis[MAXN],ic[MAXN];int T;int bel[MAXN],tot;int g[MAXN];bool ct[MAXN];int ans[MAXN],ANS;inline void be(int x,int y){mp[++cnt].to=y;mp[cnt].fro=tai[x];tai[x]=cnt;}int p(int x,int d,int f){return f*n+(x+d-1+n)%n+1;}void dfs(int x){int i,y;vis[x]=1;if(g[x]){if(g[x]<=n){if(!vis[bel[g[x]+n]]){ct[g[x]]=1;}}else{if(!vis[bel[g[x]-n]]){ct[g[x]]=1;}}}for(i=tai[x];i;i=mp[i].fro){y=mp[i].to;dfs(y);}vis[x]=0;}int main(){int i,x;scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d",&x);if(x==1){t[i]=p(i,1,0);t[n+i]=p(i,-1,1);}if(x==2){t[i]=p(i,-1,1);t[n+i]=p(i,1,0);}if(x==3){t[i]=p(i,2,0);t[n+i]=p(i,-2,1);}if(x==4){t[i]=p(i,-2,1);t[n+i]=p(i,2,0);}}for(i=1;i<=n*2;i++){if(!vis[i]){x=i;bool flag=1;T++;while(!vis[x]){vis[x]=T;if(vis[t[x]]!=T&&vis[t[x]]){flag=0;break;}x=t[x];}if(flag){tot++;while(!ic[x]){bel[x]=tot;ic[x]=1;x=t[x];}}}}memset(vis,0,sizeof(vis));for(i=1;i<=n*2;i++){if(!bel[i]){bel[i]=++tot;g[tot]=i;}}for(i=1;i<=n*2;i++){if(bel[i]!=bel[t[i]]){be(bel[t[i]],bel[i]);}}for(i=1;i<=tot;i++){if(g[i]){break;}vis[i]=1;dfs(i);vis[i]=0;}for(i=1;i<=n;i++){if(ct[i]&&ct[n+i]&&!ic[i]&&!ic[n+i]){ans[++ANS]=i;}}printf("%d\n",ANS);for(i=1;i<=ANS;i++){printf("%d ",ans[i]);}printf("\n");return 0;}/*51 2 3 4 1*/


0 0
原创粉丝点击