P3436 [POI2006]PRO-Professor Szu
题目描述
n个别墅以及一个主建筑楼,从每个别墅都有很多种不同方式走到主建筑楼,其中不同的定义是(每条边可以走多次,如果走边的顺序有一条不同即称两方式不同)。
询问最多的不同方式是多少,以及有多少个别墅有这么多方式,按照顺序输出别墅编号。
如果最多不同方式超过了36500那么都视作zawsze
输入输出样例
这题反向建个图,缩个点,跑个topo就没了?显然没有这么容易,这里说一下题目的坑点。首先这个题目是要求从n+1号结点出发,那么如果n+1号点不能到达所有的结点会怎么样呢?
看上面这张图,假设这是我们的反向图,按理说我们应该从6号点开始topo,但是5号点自始至终都不会入队,所以说4号点的入度就不可能减到0,这样的话4号点就无法进行topo,那么这样的答案也肯定是错的。要解决这个问题也很简单,就是在缩点时只缩从n+1号点能到达的结点,然后再建新图,而且建的新图中不能含有从n+1号点不能到达的结点。
1 tarjan(n+1);//这里只从n+1号点开始缩点 2 for(int i=1;i<=n+1;i++) 3 { 4 if(!dfn[i]) continue;//如果某一个点是n+1号点无法到达的,那么就不能把这个点加到图中 5 for(int j=last[i];j;j=g[j].next) 6 { 7 int v=g[j].to; 8 if(co[i]!=co[v]) 9 { 10 add1(co[i],co[v]); 11 de[co[v]]++; 12 } 13 } 14 }
但是这样还是A不了,这又是为啥呢?原来是自环惹的祸。
看上边的图,我们已经解决了五号点这个从起点无法到达的点的问题了,但是如果四号点给你来一个自环,那不好意思,你又挂了,要是按正常的缩点的话,四号点应该自己单独成为一个强联通分量,按理应该不需要管他,直接dp就好了,但是如果有自环,那么只要经过4号点,方案数就一定会变为正无穷,所以说,自环也是一定要考虑在内的,但是该怎么做呢,这个就更简单了,只需在读入的时候加个特判就好了,最后统计是把自环也当成环处理即可。
1 for(int i=1;i<=m;i++) 2 { 3 aa=read();bb=read(); 4 if(aa==bb) 5 b[aa]=1;//如果有自环,就标记一下; 6 add(bb,aa); 7 }
然而这样还是A不了,这究竟是为什么呢?后来经我计算发现36500*1000000=36500000000
然后就GG了,果断#define int long long,然后就A了……
真的是巨坑无比的一道题目。
1 #include<iostream> 2 #include<string> 3 #include<cmath> 4 #include<cstring> 5 #include<cstdio> 6 #include<stack> 7 #include<queue> 8 #define int long long 9 #define maxn 2000005 10 using namespace std; 11 12 struct edge 13 { 14 int next,to; 15 }g[maxn<<1],g1[maxn<<1]; 16 int n,m,num,tot,col,num1,aa,bb,cnt,tott,pd,de[maxn],t[maxn]; 17 long long ans; 18 long long f[maxn]; 19 int last[maxn],dfn[maxn],low[maxn],co[maxn],last1[maxn],a[maxn],b[maxn],c[maxn]; 20 stack<int>s; 21 stack<int>ss; 22 23 inline int read() 24 { 25 char c=getchar(); 26 int x=1,res=0; 27 while(c<'0'||c>'9') 28 { 29 if(c=='-') 30 x=-1; 31 c=getchar(); 32 } 33 while(c>='0'&&c<='9') 34 { 35 res=res*10+(c-'0'); 36 c=getchar(); 37 } 38 return x*res; 39 } 40 41 void add(int from,int to) 42 { 43 g[++num].next=last[from]; 44 g[num].to=to; 45 last[from]=num; 46 } 47 48 void add1(int from,int to) 49 { 50 g1[++num1].next=last1[from]; 51 g1[num1].to=to; 52 last1[from]=num1; 53 } 54 55 void tarjan(int u) 56 { 57 dfn[u]=low[u]=++tot; 58 s.push(u); 59 for(int i=last[u];i;i=g[i].next) 60 { 61 int v=g[i].to; 62 if(!dfn[v]) 63 { 64 tarjan(v); 65 low[u]=min(low[u],low[v]); 66 } 67 else if(!co[v]) 68 { 69 low[u]=min(low[u],dfn[v]); 70 } 71 } 72 if(low[u]==dfn[u]) 73 { 74 col++;cnt=0; 75 for(;;) 76 { 77 int x=s.top();s.pop(); 78 co[x]=col; 79 cnt++; 80 if(x==u) break; 81 } 82 a[col]=cnt; 83 } 84 } 85 86 void topo() 87 { 88 ss.push(co[n+1]); 89 if(c[co[n+1]]==1) 90 { 91 f[co[n+1]]=36501; 92 } 93 else 94 { 95 f[co[n+1]]=1; 96 } 97 while(ss.size()) 98 { 99 int u=ss.top();ss.pop(); 100 for(int i=last1[u];i;i=g1[i].next) 101 { 102 int v=g1[i].to; 103 if(c[v]==1) 104 { 105 f[v]=36501; 106 } 107 else 108 { 109 f[v]+=f[u]; 110 } 111 de[v]--; 112 if(de[v]==0) 113 ss.push(v); 114 } 115 } 116 } 117 118 signed main() 119 { 120 n=read();m=read(); 121 for(int i=1;i<=m;i++) 122 { 123 aa=read();bb=read(); 124 if(aa==bb) 125 b[aa]=1;//如果有自环,就标记一下; 126 add(bb,aa); 127 } 128 tarjan(n+1);//这里只从n+1号点开始缩点 129 for(int i=1;i<=n+1;i++) 130 { 131 if(!dfn[i]) continue;//如果某一个点是n+1号点无法到达的,那么就不能把这个点加到图中 132 for(int j=last[i];j;j=g[j].next) 133 { 134 int v=g[j].to; 135 if(co[i]!=co[v]) 136 { 137 add1(co[i],co[v]); 138 de[co[v]]++; 139 } 140 } 141 } 142 for(int i=1;i<=n+1;i++) 143 { 144 if(b[i]==1) 145 { 146 c[co[i]]=1; 147 } 148 if(a[co[i]]>1) 149 { 150 c[co[i]]=1; 151 } 152 } 153 topo(); 154 for(int i=1;i<=n;i++) 155 { 156 if(f[co[i]]>36500) 157 { 158 pd=1; 159 } 160 ans=max(ans,f[co[i]]); 161 } 162 if(pd==1) 163 { 164 printf("zawsze "); 165 for(int i=1;i<=n;i++) 166 { 167 if(f[co[i]]>36500) 168 { 169 t[++tott]=i; 170 } 171 } 172 printf("%d ",tott); 173 for(int i=1;i<=tott;i++) 174 { 175 printf("%d ",t[i]); 176 } 177 return 0; 178 } 179 else 180 { 181 printf("%lld ",ans); 182 for(int i=1;i<=n;i++) 183 { 184 if(f[co[i]]==ans) 185 t[++tott]=i; 186 } 187 printf("%d ",tott); 188 for(int i=1;i<=tott;i++) 189 { 190 printf("%d ",t[i]); 191 } 192 return 0; 193 } 194 }