2016北京集训测试赛river
Posted Rolling...yoyoball!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016北京集训测试赛river相关的知识,希望对你有一定的参考价值。
HINT
注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式。
[吐槽]
嗯。。看到这题的想法的话。。先想到了每个点的度为2,然后就有点不知所措了
隐隐约约想到了网络流,但并没有继续往下想了。。。
听完学长的讲评之后(%xj)个人觉得建图还是很有意思的ovo
[题解]
因为每个点到对面都有k种方式,那就想到每个点原来的点$x_0$拆成k个点$x_1$, $x_2$, $x_3$... $x_k$
然后很自然地$x_0$和拆成的点之间要连边
容量的话,因为hint里面的限制,也就是说一个点到另一个点的k中交通方式中只能选一种
(因为每个点只能到一次,而开始和结束不能用同样的方式)
这样一来容量显然就应该是1了
两岸之间的连接,就直接按照读入左岸连到右岸就好,容量也为1
(但其实因为左岸的流入流量和右岸的流出流量都有限制,中间的那条好像容量取1~ $\\infty$都可以。。。%yxq)
接着考虑最后的答案是怎么得到的,会发现其实我们最后的到的路线是若干个环,每个点的度为2(一个大概长这样的)
如此一来,就会有个大胆的想法
对于每一个左岸的$x_0$,我们连一条源点到它的容量为2的边
对于每一个右岸的$x_0$,我们连一条它到汇点的容量为2的边
这样起到一个限制了每个点的度的作用,就可以保证有环并且环内每个点的度都为2(个人感觉这点是很有意思的)
于是乎最终的到的图长这样(以样例为例)
那么现在考虑构造方案
看回之前建图的思路,很容易得到的一个结论是满流的边肯定就是要走的边
那么现在问题就变成知道一堆边然后构造方案啦
很简单粗暴的方法直接强行把每个环走一遍记录下答案就好
天数的话就看有多少个环就好啦
挫挫的代码qwq
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #define inf 2147483647 6 using namespace std; 7 const int MAXN=50*6*2+10; 8 struct xxx 9 { 10 int y,next,op,r,x; 11 }a[MAXN*2]; 12 queue<int> q; 13 int h[MAXN],lv[MAXN],id[110][10],num[MAXN]; 14 int go[MAXN][2],ans[110][110]; 15 bool vis[MAXN]; 16 int n,m,k,vs,vt,tot,tot1; 17 int add(int x,int y,int r); 18 int bfs(); 19 int dfs(int v,int o); 20 int get_ans(); 21 22 int main() 23 { 24 freopen("a.in","r",stdin); 25 26 int x,y,z; 27 scanf("%d%d%d",&n,&m,&k); 28 memset(h,-1,sizeof(h)); 29 tot=0; 30 vs=0,vt=MAXN-1; 31 for (int i=1;i<=n+n;++i) 32 for (int j=0;j<=k;++j) 33 id[i][j]=++tot,num[tot]=i; 34 tot=0; 35 for (int i=1;i<=n;++i) 36 { 37 add(vs,id[i][0],2); 38 add(id[i+n][0],vt,2); 39 for (int j=1;j<=k;++j) 40 add(id[i][0],id[i][j],1),add(id[i+n][j],id[i+n][0],1); 41 } 42 for (int i=1;i<=m;++i) 43 { 44 scanf("%d%d%d",&x,&y,&z); 45 add(id[x][z],id[y+n][z],1); 46 } 47 while (bfs()) dfs(vs,inf); 48 get_ans(); 49 } 50 51 int add(int x,int y,int r) 52 { 53 a[++tot].y=y; a[tot].next=h[x]; h[x]=tot; a[tot].r=r; a[tot].op=tot+1; 54 a[++tot].y=x; a[tot].next=h[y]; h[y]=tot; a[tot].r=0; a[tot].op=tot-1; 55 } 56 57 int bfs() 58 { 59 while (!q.empty()) q.pop(); 60 memset(lv,0,sizeof(lv)); 61 q.push(vs); 62 lv[vs]=1; 63 int v,u; 64 while (!q.empty()) 65 { 66 v=q.front(); q.pop(); 67 for (int i=h[v];i!=-1;i=a[i].next) 68 { 69 u=a[i].y; 70 if (lv[u]||!a[i].r) continue; 71 q.push(u); 72 lv[u]=lv[v]+1; 73 if (u==vt) return true; 74 } 75 } 76 return false; 77 } 78 79 int dfs(int v,int o) 80 { 81 if (v==vt||o==0) return o; 82 int u,flow,ret=0; 83 for (int i=h[v];i!=-1;i=a[i].next) 84 { 85 u=a[i].y; 86 if (lv[u]!=lv[v]+1) continue; 87 flow=dfs(u,min(a[i].r,o)); 88 if (flow) 89 { 90 a[i].r-=flow; 91 a[a[i].op].r+=flow; 92 ret+=flow; 93 o-=flow; 94 if (!o) break; 95 } 96 } 97 return ret; 98 } 99 100 int get_ans() 101 { 102 int x,y,pre; 103 //go[i]记录与i相连的两个点 104 for (int i=1;i<=n;++i) 105 for (int j=1;j<=k;++j) 106 for (int tmp=h[id[i][j]];tmp!=-1;tmp=a[tmp].next) 107 { 108 if (a[tmp].r||a[tmp].y==id[i][0]) continue; 109 y=num[a[tmp].y]; 110 if (!go[i][0]) go[i][0]=y; 111 else go[i][1]=y; 112 113 if (!go[y][0]) go[y][0]=i; 114 else go[y][1]=i; 115 } 116 memset(vis,false,sizeof(vis)); 117 int cnt=0; 118 for (int i=1;i<=n;++i) 119 { 120 if (vis[i]) continue; 121 ++cnt; 122 //将每个环走一遍 123 pre=i,x=go[i][0]; 124 ans[cnt][++ans[cnt][0]]=i; 125 vis[i]=true; 126 while (x!=i) 127 { 128 vis[x]=true; 129 ans[cnt][++ans[cnt][0]]=x; 130 if (pre==go[x][0]) pre=x,x=go[x][1]; 131 else pre=x,x=go[x][0]; 132 } 133 ans[cnt][++ans[cnt][0]]=x; 134 } 135 printf("%d\\n",cnt); 136 //因为建图的方式所以左右岸肯定是交错来的 137 for (int i=1;i<=cnt;++i) 138 { 139 printf("%d ",ans[i][0]); 140 for (int j=1;j<=ans[i][0];++j) 141 if (j&1) printf("L%d ",ans[i][j]); 142 else printf("R%d ",ans[i][j]-n); 143 printf("\\n"); 144 } 145 }
以上是关于2016北京集训测试赛river的主要内容,如果未能解决你的问题,请参考以下文章