北京集训:20180326

爆零蒟蒻今天终于滚粗了......
也就是说,这是北京集训的最后一篇题解了。之后的比赛,我已经没有访问权限了。


T1:


考虑一个暴力做法:f[i][j]表示字符串区间[i,j]的最大等级。
如果k级字符串[i,j]包含一个k-1级字符串s,且s没有达到[i,j]的首(或者尾)的话,我们去掉[i,j]的首(或者尾),剩下的仍然是一个k级字符串。
所以我们可以暴力dp,f[i][j]=max(f[i+1][j],f[i][j-1],max(f[substr])+1)。
而后面的那个可以用后缀自动机枚举出现两次及以上的子串,总复杂度O(n^3)。
显然这并不是正解(然而这对正解有很大的帮助)(要不然我也不会写他)。
考虑我们这样增量(减量?不!)去掉首尾字符的过程,我们一定能保证一个k级字符串的首尾都是一个k-1级字符串。
尾相同的子串有怎样的性质呢?他会出现在当前串的parent树的祖先上。
于是我们可以用f[i]表示后缀自动机第i个节点及其祖先节点中,最大的等级,g[i]表示取到这个等级,所在的最短的节点。
为什么这样做正确?因为对于结尾更靠后的串,我们不计算他的贡献也没有关系是吧,反正他能转移到的串的子串一定能由一个结尾更靠前的串转移到。
转移显然用最短的节点转移最优。我们暴力找头上的那个串出现的位置,看看是否可行即可。
这样我们get到了n^2暴力......
考虑我们现在复杂度的瓶颈在哪里?维护right集合和查询的过程。于是我们可以反向建立主席树并进行启发式合并,复杂度O(nlogn)。
(其实我感觉这东西时间和空间复杂度都是O(nlog^2n)的,只不过跑不满罢了)
代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 #define debug cerr
  7 using namespace std;
  8 const int maxn=4e5+1e2;
  9 
 10 char in[maxn>>1];
 11 int li,ans=1;
 12 
 13 struct PersistentSegmentTree {
 14     static const int maxe = maxn * 25;
 15     int siz[maxe],lson[maxe],rson[maxe],cnt;
 16     
 17     inline void insert(int &pos,int l,int r,const int &tar) {
 18         if( !pos ) pos = ++cnt;
 19         siz[pos] = 1;
 20         if( l == r ) return;
 21         const int mid = ( l + r ) >> 1;
 22         if( tar <= mid ) return insert(lson[pos],l,mid,tar);
 23         else return insert(rson[pos],mid+1,r,tar);
 24     }
 25     inline int merge(int p1,int p2,int l,int r) {
 26         if( ! ( siz[p1] && siz[p2] ) ) return siz[p1] ? p1 : p2;
 27         int ret = ++cnt; siz[ret] = siz[p1] + siz[p2];
 28         if( l == r ) return ret;
 29         const int mid = ( l + r ) >> 1;
 30         lson[ret] = merge(lson[p1],lson[p2],l,mid);
 31         rson[ret] = merge(rson[p1],rson[p2],mid+1,r);
 32         return ret;
 33     }
 34     inline int query(int pos,int l,int r,const int &ll,const int &rr) {
 35         if( !pos ) return 0;
 36         if( ll <= l && r <= rr ) return siz[pos];
 37         const int mid = ( l + r ) >> 1;
 38         if( rr <= mid ) return query(lson[pos],l,mid,ll,rr);
 39         if( ll > mid ) return query(rson[pos],mid+1,r,ll,rr);
 40         return query(lson[pos],l,mid,ll,rr) + query(rson[pos],mid+1,r,ll,rr);
 41     }
 42 }tree;
 43 
 44 namespace SAM {
 45     int ch[maxn][26],fa[maxn],len[maxn],deg[maxn],last,root,cnt;
 46     int rit[maxn],pos[maxn],roots[maxn],bst[maxn],f[maxn];
 47     int seq[maxn],qlen;
 48     
 49     inline int NewNode(int li) {
 50         len[++cnt] = li;
 51         return cnt;
 52     }
 53     inline void extend(int x,int at) {
 54         int p = last;
 55         int np = NewNode(len[p]+1); rit[np] = pos[np] = at;
 56         while( p && !ch[p][x] ) ch[p][x] = np , p = fa[p];
 57         if( !p ) fa[np] = root;
 58         else {
 59             int q = ch[p][x];
 60             if( len[q] == len[p] + 1 ) fa[np] = q;
 61             else {
 62                 int nq = NewNode(len[p]+1);
 63                 memcpy(ch[nq],ch[q],sizeof(ch[q])) , fa[nq] = fa[q] , pos[nq] = pos[q];
 64                 fa[np] = fa[q] = nq;
 65                 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p];
 66             }
 67         }
 68         last = np;
 69     }
 70     inline void build() {
 71         last = root = NewNode(0);
 72         for(int i=1;i<=li;i++) extend(in[i]-'a',i);
 73     }
 74     inline void topo() {
 75         for(int i=1;i<=cnt;i++) if( fa[i] ) ++deg[fa[i]];
 76         queue<int> q;
 77         for(int i=1;i<=cnt;i++) if( !deg[i] ) q.push(i);
 78         while( q.size() ) {
 79             const int pos = q.front(); q.pop() , seq[++qlen] = pos;
 80             if( pos == root ) continue;
 81             if( rit[pos] ) {
 82                 int t = 0;
 83                 tree.insert(t,1,li,rit[pos]);
 84                 roots[pos] = tree.merge(roots[pos],t,1,li);
 85             }
 86             roots[fa[pos]] = tree.merge(roots[fa[pos]],roots[pos],1,li);
 87             if( !--deg[fa[pos]] ) q.push(fa[pos]);
 88         }
 89         reverse(seq+1,seq+1+qlen);
 90     }
 91     inline void getans() {
 92         f[root] = 1 , bst[root] = root;
 93         for(int i=2;i<=qlen;i++) {
 94             const int now = seq[i] , milen = len[fa[now]] + 1;
 95             if( fa[now] == root ) {
 96                 f[now] = 1 , bst[now] = now;
 97             } else {
 98                 bst[now] = bst[fa[now]] , f[now] = f[bst[now]];
 99                 const int ql = pos[now] - len[now] + len[bst[now]];
100                 const int qr = pos[now] - milen + len[bst[now]];
101                 if( tree.query(roots[bst[now]],1,li,ql,qr) ) f[now]++ , bst[now] = now;
102             }
103             ans = max( ans , f[now] );
104         }
105     }
106 }
107 
108 int main() {
109     scanf("%s",in+1) , li = strlen(in+1);
110     SAM::build() , SAM::topo();
111     SAM::getans();
112     printf("%d\n",ans);
113     return 0;
114 }
View Code


T2:


显然不管是不是DAG,直接跑最小割都是WA的,不服见下图。
另外显然环上的边是一定不能割的,否则绕环一圈再走的路径上会有两条被割掉的边,我们可以先tarjan大力缩环变成DAG。
考虑如何防止一条路径上的边被割多次,我们能让这条路径上所有靠后的点向靠前的点连边容量inf,如果割掉后面的边且割掉前面的边相当于没割......
这建图点数O(n),边数O(m^2)。
考虑怎么优化,由于inf具有传递性,所以我们让每个靠后的点单独向前面的点连边就行了,也就是,对于每一条边连接容量inf的反向边。
对于环的话,由于inf边的存在,我们显然不会割环上的边,于是连tarjan也省了。
然而这样是不行的,对于不在1~n路径上的边,不能连inf边,否则跑出的答案会比正确答案大(反例见上图)。
关于无解,直接判断最小割是否为inf就好了。
代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 typedef long long int lli;
  7 using namespace std;
  8 const int maxn=1e3+1e2,maxe=2e3+1e2;
  9 const lli inf=0x3f3f3f3f3f3f3f3fll;
 10 
 11 namespace Flow {
 12     int s[maxn<<1],t[maxe<<2],nxt[maxe<<2],cnt=1;
 13     lli f[maxe<<2];
 14     int dep[maxn],st,ed;
 15     
 16     inline void coredge(int from,int to,lli flow) {
 17         t[++cnt] = to , f[cnt] = flow ,
 18         nxt[cnt] = s[from] , s[from] = cnt;
 19     }
 20     inline void singledge(int from,int to,lli flow) {
 21         coredge(from,to,flow) , coredge(to,from,0);
 22     }
 23     inline bool bfs() {
 24         memset(dep,-1,sizeof(dep)) , dep[st] = 0;
 25         queue<int> q; q.push(st);
 26         while( q.size() ) {
 27             const int pos = q.front(); q.pop();
 28             for(int at=s[pos];at;at=nxt[at])
 29                 if( f[at] && !~dep[t[at]] )
 30                     dep[t[at]] = dep[pos] + 1 , q.push(t[at]);
 31         }
 32         return ~dep[ed];
 33     }
 34     inline lli dfs(int pos,lli flow) {
 35         if( pos == ed ) return flow;
 36         lli ret = 0 , now = 0;
 37         for(int at=s[pos];at;at=nxt[at])
 38             if( f[at] && dep[t[at]] > dep[pos] ) {
 39                 now = dfs(t[at],min(flow,f[at]));
 40                 ret += now , flow -= now ,
 41                 f[at] -= now , f[at^1] += now;
 42                 if( !flow ) return ret;
 43             }
 44         if( !ret ) dep[pos] = -1;
 45         return ret;
 46     }
 47     inline lli dinic() {
 48         lli ret = 0 , now = 0;
 49         while( bfs() ) {
 50             while( now = dfs(st,inf) ) {
 51                 if( now == inf ) return -1; //No solution .
 52                 ret += now;
 53             }
 54         }
 55         return ret;
 56     }
 57     inline void reset(int n) {
 58         memset(s,0,sizeof(s))  ,cnt = 1;
 59         st = 1 , ed = n;
 60     }
 61 }
 62 
 63 struct Graph {
 64     int s[maxn],t[maxe<<1],nxt[maxe<<1],vis[maxn],cnt;
 65     
 66     inline void addedge(int from,int to) {
 67         t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt;
 68     }
 69     inline void dfs(int pos) {
 70         if( vis[pos] ) return;
 71         vis[pos] = 1;
 72         for(int at=s[pos];at;at=nxt[at]) dfs(t[at]);
 73     }
 74     inline void reset() {
 75         memset(s,0,sizeof(s)) , memset(vis,0,sizeof(vis)) , cnt = 0;
 76     }
 77 }gra,inv;
 78 
 79 int x[maxe],y[maxe],f[maxe];
 80 
 81 inline void build(int m) {
 82     for(int i=1;i<=m;i++) {
 83         Flow::singledge(x[i],y[i],f[i]);
 84         if( gra.vis[x[i]] && inv.vis[y[i]] ) Flow::singledge(y[i],x[i],inf);
 85     }
 86 }
 87 
 88 inline void reset(int n) {
 89     Flow::reset(n);
 90     gra.reset() , inv.reset();
 91 }
 92 
 93 int main() {
 94     static int T,n,m;
 95     scanf("%d",&T);
 96     while(T--) {
 97         scanf("%d%d",&n,&m) , reset(n);
 98         for(int i=1;i<=m;i++) {
 99             scanf("%d%d%d",x+i,y+i,f+i);
100             gra.addedge(x[i],y[i]) , inv.addedge(y[i],x[i]);
101         }
102         gra.dfs(1) , inv.dfs(n);
103         build(m);
104         printf("%lld\n",Flow::dinic());
105     }
106     return 0;
107 }
View Code


T3:


直接放官方题解吧......


代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define debug cout
 6 using namespace std;
 7 const int maxn=1e6+1e2;
 8 
 9 int s[maxn],t[maxn<<1],nxt[maxn<<1],fa[maxn],w[maxn],sons[maxn];
10 int n,a,b;
11 
12 inline  void coredge(int from,int to) {
13     static int cnt = 0;
14     t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt;
15 }
16 inline void doubledge(int a,int b) {
17     coredge(a,b) , coredge(b,a);
18 }
19 inline void update(int &mx,int &sec,const int &now) {
20     if( now >= mx ) sec = mx , mx = now;
21     else if( now > sec ) sec = now;
22 }
23 inline void dfs(int pos) {
24     int mx = 0 , sec = 0;
25     for(int at=s[pos];at;at=nxt[at])
26         if( t[at] != fa[pos] ) {
27             fa[t[at]] = pos , ++sons[pos];
28             dfs(t[at]) , update(mx,sec,w[t[at]]);
29         }
30     w[pos] = sec + sons[pos];
31 }
32 inline bool check(int lim) {
33     int fs = 0 , tim = 0 , ret = 0 , now = a , last = -1;
34     for(int i=a;i!=b;i=fa[i]) fs += sons[i] - ( i != a );
35     while( now != b ) {
36         ++tim;
37         int sum = 0;
38         for(int at=s[now];at;at=nxt[at])
39             if( t[at] != fa[now] && t[at] != last) {
40                 if( w[t[at]] + ret + fs > lim ) ++sum , --tim;
41                 if( tim < 0 ) return 0; // No enough time .
42             }
43         ret += sum , fs -= sons[now] - ( now != a );
44         last = now , now = fa[now];
45         if( ret > lim ) return 0;
46     }
47     return ret <= lim;
48 }
49 inline int bin() {
50     int ll = -1 , rr = n << 1 , mid;
51     while( rr > ll + 1 ) {
52         mid = ( ll + rr ) >> 1;
53         if( check(mid) ) rr = mid;
54         else ll = mid;
55     }
56     return rr;
57 }
58 
59 int main() {
60     scanf("%d%d%d",&n,&b,&a);
61     for(int i=1,a,b;i<n;i++) {
62         scanf("%d%d",&a,&b) , doubledge(a,b);
63     }
64     dfs(b);
65     printf("%d\n",bin());
66     return 0;
67 }
View Code


再有十几天就省选了,我这么弱,九成是要退役了吧。
大概无论如何也不会坚持了吧。“你学下去”,想像BE里的红一样用生命挽救别人,然而还有谁值得我这样做呢?
直到现在才发现,看起来很正常的我,其实是黑的......

posted @ 2018-03-27 20:10  Cmd2001  阅读(143)  评论(0编辑  收藏  举报