【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 }

时间: 2024-07-29 09:50:30

【2016北京集训测试赛】river的相关文章

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

【2016北京集训测试赛(八)】直径

注意:时限更改为4s 题解 考虑最原始的直径求法:找到离根节点(或离其他任意一点)最远的节点far1,再从far1出发找到离far1最远的节点far2,far1至far2的距离即为直径. 题目中提到了将原树的子树复制成新子树这一操作,显然如果我们将子树真正复制出来是会爆炸的. 实际上我们可以将每棵新子树中,真正有用的节点提取出来,以简化每个子树的结构,再按照题目的要求连接各个新子树. 我们用虚树来重构每一棵子树.每棵子树的虚树的关键点应至少包含: 子树的根节点. 这棵子树内部的直径的两端节点.

【2016北京集训测试赛(七)】自动机 (思考题)

Time Limit: 1000 ms Memory Limit: 256 MB Description Solution 这是一道看起来令人毫无头绪的题,然而确实十分简单巧妙TAT. 题目要求所有点执行相同指令后都回到初始状态. 我们先来考虑只有两种状态的情况:初始状态$T_0$与另一个状态$T_x$. 这样,我们可以通过一个二元记忆化深搜,来得到一种方案A,使得$T_0$回到$T_0$,且$T_x$回到$T_0$.如果这个方案都不存在,那么此时无解. 现在我们知道,执行方案A后,$T_x$与

2016北京集训测试赛(十七)- 小结

先说一下总体的情况. 场上期望得分 50 + 40 + 30 = 120 , 最后得分 50 + 0 + 30 = 80 , 实际上自己能力所及能做到的 50 + 65 + 30 = 145 分. 第二题爆零是因为我开始写了一个做法, 后来发现这个做法是错的, 然后开始随机化, 调着调着突然发现只有一分钟了, 然后自己把之前调的改回来, 然后发现怎么全都输出 0 ??? Excuse me ?? 原本不用随机化的做法可以拿 40 分, 如果结合上暴力就有 65 了. 这几天打起比赛来还是暴露了许

【2016北京集训测试赛】azelso(unfinished)

[吐槽] 首先当然是要orzyww啦 以及orzyxq奇妙顺推很强qwq 嗯..怎么说呢虽然说之前零零散散做了一些概d的题目但是总感觉好像并没有弄得比较明白啊..(我的妈果然蒟蒻) 这题的话可以说是难得的一道搞得比较清楚的概d题目吧记录一下还是挺有意思的ovo 当然咯..显然考场上并没有推出来..嗯qwq [题解] 看到说要求期望的距离,然后总的长度又被分成了一段一段的(各个事件) 所以就有一个比较直接的想法:将每一段期望走的次数算出来然后再乘上每一段的距离,加起来就是答案啦 那么现在问题来了怎

[2016北京集训测试赛(一)]奇怪的树-[树链剖分]

Description Solution 对于操作1,不论选了哪个点为a,最后反转颜色的点集都只有两种类型(显然啦). 暴力解法:对每个操作3,从a向上直到根节点,每到一个节点记录(它父亲的黑点数减去自己的黑点数)*父亲节点的编号.另外,还要记录a子树内的黑点.. 这种O(n2)的做法肯定会爆,考虑优化.由于这是一棵静态树,考虑树链剖分. 需要记录一个数组re[x][0/1][0/1].第2维表示深度的奇偶,第3维表示点的颜色.例如:re[x][0][0]记录的是初始情况下以x为根的子树中深度为

[2016北京集训测试赛(五)]打地鼠-[思考题]

Description Solution 我们先只考虑一只地鼠的情况,依题意得,在某一个时刻该地鼠的可能停留位置是一个公差为2的等差数列.我们设这个等差数列的两端为[L,R].则如果区间[L+1,R-1]的格子被打实际上是不会影响L和R的(列一个等差数列实际模拟一下就发现啦).而如果格子L被打,则L+2:如果格子R被打,则R-2.打了格子后,别忘了L--,R++. 嗯根据以上性质,我们可以知道,地鼠1,3,5,7,9...的L是非递减的,地鼠2,4,6,8,10...的L也是非递减的. 然后看一

[2016北京集训测试赛15]statement-[线段树+拆环]

Description Solution 由于题目要求,将a[i]->b[i](边权为i)后所得的图应该是由森林和环套树组合而成. 假如是树形结构,所有的t[i]就直接在线段树t[i]点的dfs序(即in[t[i]],out[t[i]]区间)处记录t[i]点的深度. 这样,针对所有的f[i],在线段树上查找所有包含in[f[i]]点的区间所记录的最大深度d.(这个深度就是在离f[i]最近并且已经验证了是真命题的祖先的深度) 然后用倍增算出f[i]向上到深度d,所经过的编号最大值c.ans=min

[2016北京集训测试赛17]crash的游戏-[组合数+斯特林数+拉格朗日插值]

Description Solution 核心思想是把组合数当成一个奇怪的多项式,然后拉格朗日插值..:哦对了,还要用到第二类斯特林数(就是把若干个球放到若干个盒子)的一个公式: $x^{n}=\sum _{i=0}^{n}C(n,i)*i!*S(i,x)$ 围观大佬博客(qaq公式太难打了) Code #include<iostream> #include<cstdio> #include<cstring> #include<cmath> using na