2020牛客暑期多校第一场(AH)
题目说明:
A.B-Suffix Array(后缀数组)
H.Minimum-cost Flow(费用流)
A.B-Suffix Array
题目大意:给你一个长度为n的字符串,只包含'a'和'b',然后构造一个序列,序列中每个元素为之前该位置字母相同的位置的最短距离,如:abab构造出的序列就是0022,问对每个后缀构造这样的序列,每个序列的排名,请输出排名从小到大后缀的长度。
输入
2 aa 3 aba 5 abaab
输出
2 1 3 2 1 5 4 2 1 3
emmm,应该一眼就看出来了是个后缀数组。。。然后就不会了。。。它的后缀是在不断变化的,这就很头痛了。然后最后的题解:结论题。。。。就是构造个c序列,里面的每个元素的值为在后面离它最近的相同字母的位置和该位置的差值。然后对c序列跑个后缀数组。。。
需要注意的是后缀数组里面的swap要换成手动for一遍,不然会T掉。。。
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=1e5+10; char ss[mac]; int rk[mac],sa[mac],copy_rk[mac]; int id[mac],cnt[mac],px[mac],n,s[mac]; bool cmp(int x,int y,int len) { return copy_rk[x]==copy_rk[y]&©_rk[x+len]==copy_rk[y+len]; } void get_sa() { int m=n+1; for (int i=1; i<=m; i++) rk[i]=sa[i]=id[i]=copy_rk[i]=cnt[i]=px[i]=0; for (int i=1; i<=n; ++i) ++cnt[rk[i]=s[i]]; for (int i=1; i<=m; ++i) cnt[i]+=cnt[i-1]; for (int i=n; i>=1; --i) sa[cnt[rk[i]]--]=i; int p; for (int len=1; len<n; len<<=1,m=p){ p=0; for (int i=n; i>n-len; --i) id[++p]=i; for (int i=1; i<=n; ++i) if (sa[i]>len) id[++p]=sa[i]-len; for (int i=1; i<=m; i++) cnt[i]=0; for (int i=1; i<=n; ++i) ++cnt[px[i]=rk[id[i]]]; for (int i=1; i<=m; ++i) cnt[i]+=cnt[i-1]; for (int i=n; i>=1; --i) sa[cnt[px[i]]--]=id[i]; //swap(rk,copy_rk); for (int i=1; i<=n; i++) copy_rk[i]=rk[i]; p=0; for (int i=1; i<=n; ++i) rk[sa[i]]=cmp(sa[i],sa[i-1],len)?p:++p; if (p==n) break;//不加会T } } int main() { while (~scanf ("%d",&n)){ scanf ("%s",ss+1); int ida=0,idb=0; for (int i=n; i>=1; --i){ if (ss[i]=='a'){ if (ida) s[i]=ida-i; else s[i]=n; ida=i; } else { if (idb) s[i]=idb-i; else s[i]=n; idb=i; } } s[n+1]=n+1;n++; get_sa(); for (int i=n-1; i>=1; --i) printf("%d ",sa[i]);printf("\n"); } return 0; }
H.Minimum-cost Flow
题目大意:n个顶点m条边,第$i$个边链接了$a_i$ $b_i$ 花费为$w_i$,然后q次询问,每次询问当每条边边的容量为$\frac{u_i}{v_i}$的时候,从1到n流1的总容量的最小花费。
输入
2 1 1 2 2 1 1 1 2 2 1 2 1 1 2 2 3 1 2 2 3 1 4
输出
2/1 3/2 4/3 NaN
emmm,确实是费用流,但难搞的是边的容量会变,但实际上由于边的容量时一致的,所以我们可以先跑边为单位容量1的时候的费用流,最后可以将边的容量的变化对应到总容量的变化来计算。比若说对于$\frac{4}{7}$这个容量的边,那么我们就需要找到能够跑满2次容量的路径,即能够找到2条单位增广路。
我们需要知道的是,费用流每一次会找到一条增广路。我们设第$i$个增广路的费用为$cost_i$那么,需要跑满$k$次满容量的时候费用就是对前面k条路做个前缀和。
接下来就是有点操作的时候了,对于计算总的花费,我们容量1是对于分子而言的,也就是说最后我们还得乘上分子,然后对分母同时除以最大公约数,如下所示:
for (int i=1; i<=q; i++) { int u,v; scanf ("%d%d",&u,&v); if (u==0) { printf("NaN\n"); continue; } int num=v/u; ll ans; if (v%u) num++; if (num>num_rod) { printf("NaN\n"); continue; } if (v%u==0) ans=1LL*sum[num]*u; else ans=1LL*sum[num-1]*u,ans+=1LL*rod[num]*(v%u);//前面num-1条路跑满了,最后一个单独计算 ll p=__gcd(ans,1LL*v); printf("%lld/%lld\n",ans/p,v/p); }
以下是AC代码:
#include <bits/stdc++.h> using namespace std; #define IOS ios::sync_with_stdio(false) const int mac = 150 + 10; const int inf = 1e8 + 10; typedef long long ll; int n, m; struct edge { int next, to, f, w; }eg[mac << 1]; int head[mac], cnt = 1; int dis[mac], pre[mac], vis[mac], flows[mac]; int s, t, maxflow, flag[mac],rod[mac],num_rod; ll sum[mac]; inline void add(int u, int v, int f, int w) { eg[++cnt].to = v; eg[cnt].next = head[u]; eg[cnt].f = f; eg[cnt].w = w; head[u] = cnt; eg[++cnt].to = u; eg[cnt].next = head[v]; eg[cnt].f = 0; eg[cnt].w = -w; head[v] = cnt; } void update(int x, int flow) { eg[pre[x]].f -= flow; eg[pre[x] ^ 1].f += flow; if (eg[pre[x] ^ 1].to)update(eg[pre[x] ^ 1].to, flow); } inline int spfa() { memset(vis, 0, sizeof(vis)); queue<int>q; for (int i = 1; i <= n; i++) dis[i] = inf; flows[s] = inf; dis[s] = 0; q.push(s); vis[s] = 1; while (!q.empty()){ int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u]; i!=-1; i = eg[i].next){ int v = eg[i].to; if (dis[v] > dis[u] + eg[i].w && eg[i].f){ dis[v] = dis[u] + eg[i].w; pre[v] = i; flows[v] = min(flows[u], eg[i].f); if (!vis[v]){ vis[v] = 1; q.push(v); } } } } if (dis[t] == inf) return inf; maxflow += flows[t]; update(t, flows[t]); return flows[t] * dis[t]; } inline void EK() { int per_rod = 0; while (1){ int per_rod = spfa(); if (per_rod == inf) return; rod[++num_rod]=per_rod; } } int main() { IOS; while (~scanf ("%d%d",&n,&m)){ memset(head,-1,sizeof head); s=1,t=n,num_rod=0,cnt=1; for (int i=1; i<=m; i++){ int u,v,w; scanf ("%d%d%d",&u,&v,&w); add(u,v,1,w); } EK(); for (int i=1; i<=num_rod; i++) sum[i]=sum[i-1]+rod[i]; int q; scanf ("%d",&q); for (int i=1; i<=q; i++){ int u,v; scanf ("%d%d",&u,&v); if (u==0) {printf("NaN\n"); continue;} int num=v/u; ll ans; if (v%u) num++; if (num>num_rod) {printf("NaN\n"); continue;} if (v%u==0) ans=1LL*sum[num]*u; else ans=1LL*sum[num-1]*u,ans+=1LL*rod[num]*(v%u);//前面num-1条路跑满了,最后一个单独计算 ll p=__gcd(ans,1LL*v); printf("%lld/%lld\n",ans/p,v/p); } } return 0; }