A.B-Suffix Array(待补)
题意:
将字符串的每个后缀化成数组,然后对数组进行字典序排序,求排序顺序
题解:
F.Infinite String Comparision
题意:
给定两个序列和,将两者延伸至无限长,询问两者的大小关系
题解:
第一想法是找两者长度的,发现好像会爆内存。再想想可知只需要延伸至比较即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { string a, b; while (cin >> a >> b) { int la = a.length(), lb = b.length(); ll lc = 2 * max(la, lb); string na = "", nb = ""; while (na.length() < lc) na += a; while (nb.length() < lc) nb += b; na = na.substr(0, lc); nb = nb.substr(0, lc); if (na < nb) cout << "<\n"; else if (na == nb) cout << "=\n"; else cout << ">\n"; } return 0; }
M.Minimum-cost Flow
题意:
给定一个个点和条边的网络,每条边有个费用。现在给出个询问,每个询问给出和两个整数,表示每条边的容量为,要求从源点到汇点需要有单元的流量,询问最小的费用是多少,结果表示为分数形式
题解:
很明显的最小费用流,如果知道就好做了,其中表示在容量为中流量为所需的费用。那么原本建图时需要设置容量为,现在只需设置为,最终所需流量也不为,而是,当然最终答案还要再乘上,建完图剩下就是跑一遍最小费用流即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 55, M = 105, INF = 0x3f3f3f3f; struct edge { int y, nxt, c, w; } e[M * 2]; int d[N], head[N], pre[N], infc[N], cross[N], s[N]; int tot, n, m, tp; bool inq[N]; void init() { tot = tp = 0; memset(head, -1, sizeof(head)); } void add(int x, int y, int c, int w) { e[tot] = edge{y, head[x], c, w}; head[x] = tot++; e[tot] = edge{x, head[y], 0, -w}; head[y] = tot++; } bool spfa() { memset(d, INF, sizeof(d)); memset(inq, 0, sizeof(inq)); queue<int> q; d[1] = 0; infc[1] = INF; q.push(1); while (q.size()) { int x = q.front(); q.pop(); inq[x] = 0; for (int i = head[x]; ~i; i = e[i].nxt) { int y = e[i].y; if (e[i].c && d[y] > d[x] + e[i].w) { d[y] = d[x] + e[i].w; infc[y] = min(infc[x], e[i].c); pre[y] = i; if (!inq[y]) q.push(y), inq[y] = 1; } } } return d[n] != INF; } void ek() { int x = n; while (x != 1) { int i = pre[x]; e[i].c -= infc[n]; e[i ^ 1].c += infc[n]; x = e[i ^ 1].y; } cross[++tp] = d[n]; s[tp] = s[tp - 1] + d[n]; } int main() { while (scanf("%d%d", &n, &m) != EOF) { init(); for (int i = 1; i <= m; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); add(u, v, 1, w); } while (spfa()) ek(); int q; scanf("%d", &q); while (q--) { ll u, v; scanf("%lld%lld", &u, &v); ll a = 0, b = v; if (tp * u < v) puts("NaN"); else { a += s[v / u] * u; if (v % u != 0) a += (v % u) * cross[v / u + 1]; printf("%lld/%lld\n", a / __gcd(a, b), b / __gcd(a, b)); } } } return 0; }
I.1 or 2
题意:
给定一个个点和条边的图,对于每个节点给出一个,表示与该结点相连的边数为多少,要求从条边中选取一些使得满足条件,判断是否存在满足条件的方案
题解:
赛时想了一种最大流的方案,但是给我hack掉了,赛后发现好多人都是这么过的,拉了个板子试试好像确实能过,造了组数据就给hack了。好像用带花树算法可以搞出来,下次研究下,先放个学习链接。戳我~
这题好像还是HDU3551的原题可以去参考一下这题的题解,是这题的加强版戳我~
一般图匹配题单
附上能hack掉最大流的样例
4 6 2 2 2 1 1 2 1 3 1 4 2 3 2 4 3 4 No
带花树
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e3 + 5, M = 2e4 + 5, inf = 0x3f3f3f3f, mod = 1e9 + 7; int d1[N], d2[N], n, m; struct Blossom_Tree{ vector<int>e[N]; queue<int>q; int mh[N],pre[N],s[N],id[N],vis[N],n,num; void init(int nn){ //初始化 n=nn,num=0; for(int i=1;i<=n;i++) e[i].clear(),mh[i]=pre[i]=vis[i]=s[i]=id[i]=0; } void add(int u,int v){ e[u].push_back(v),e[v].push_back(u); } int find(int x){ return s[x]==x?x:s[x]=find(s[x]);} int LCA(int x,int y){ //求LCA. for(++num;;swap(x,y)) if(x){ if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. id[x]=num,x=pre[mh[x]]; //沿增广路径的非匹配边向上走直到找到祖先. } } void blossom(int x,int y,int rt){ //缩点(开花) O(n) while(find(x)!=rt){ pre[x]=y,y=mh[x]; if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. if(find(x)==x) s[x]=rt;//只对根处理. if(find(y)==y) s[y]=rt; x=pre[y]; } } bool aug(int st){ //求增广路径.O(n) for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; while(!q.empty()) q.pop(); q.push(st),vis[st]=1; while(!q.empty()){ int u=q.front();q.pop(); for(auto v:e[u]){ if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. if(!vis[v]){ //如果没有被染色 vis[v]=2,pre[v]=u; if(!mh[v]){ //如果未被匹配,就进行匹配 for(int x=v,last;x;x=last) //增广路取反 last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x; return 1; } vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. } else{ int rt=LCA(u,v); //找到LCA ,然后进行缩点. blossom(u,v,rt),blossom(v,u,rt); } } } return 0; } int cal(){ int ans=0; for(int i=1;i<=n;i++) if(!mh[i]&&aug(i)) ans++; //最大匹配. return ans; } }DHS; int main() { while (scanf("%d%d", &n, &m)!=EOF) { int tot = 0; for (int i = 1, x; i <= n; i++) { scanf("%d", &x); if (x == 1) d1[i] = ++tot, d2[i] = d1[i]; else d1[i] = ++tot, d2[i] = ++tot; } DHS.init(2 * m + tot); n = 2 * m + tot; for (int i = 1, u, v; i <= m; i++) { scanf("%d%d", &u, &v); int e1 = ++tot, e2 = ++tot; DHS.add(e1, e2); DHS.add(e1, d1[u]); DHS.add(e1, d2[u]); DHS.add(e2, d1[v]); DHS.add(e2, d2[v]); } puts(DHS.cal() * 2 == tot ? "Yes" : "No"); } return 0; }
附上错的最大流代码
#include <cstdio> #include <vector> #include <algorithm> #include <cstring> #include <queue> using namespace std; typedef long long LL; const int INF=0x3f3f3f3f; const int MAXN = 2010,MAXM = 1200010; namespace maxflow { typedef int type; const type INF=0x3f3f3f3f; struct node { int to; type cap; int next; node(int t=0,type c=0,int n=0):to(t),cap(c),next(n) {}; } edge[MAXM]; int head[MAXN],tot; void addedge(int from,int to,type cap,type rcap=0) { edge[tot]=node(to,cap,head[from]); head[from]=tot++; edge[tot]=node(from,rcap,head[to]); head[to]=tot++; } int dep[MAXN],cur[MAXN];//当前弧优化 bool bfs(int s,int t,int n) { static int Q[MAXN],ST,ED; memset(dep+1,0,n*sizeof(int)); ST=0; ED=-1; Q[++ED]=s; dep[s]=1; while (ST<=ED) { int u=Q[ST++]; for (int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].to; if (!dep[v]&&edge[i].cap) { Q[++ED]=v; dep[v]=dep[u]+1; } } } return (dep[t]!=0); } type dfs(int x,const int &t,type flow=INF) { if (x==t||flow==0) return flow; type ret=0; for (int i=cur[x]; i!=-1; i=edge[i].next) { if (dep[x]+1==dep[edge[i].to]&&edge[i].cap){ type f=dfs(edge[i].to,t,min(flow,edge[i].cap)); edge[i].cap-=f; edge[i^1].cap+=f; ret+=f; flow-=f; cur[x]=i; if (flow==0) break; } } if (!ret) dep[x]=0; return ret; } type maxflow(int s,int t,int n) {//Dinic type ret=0; while (bfs(s,t,n)) { type f; memcpy(cur+1,head+1,n*sizeof(int)); while ((f=dfs(s,t))>0) ret+=f; } return ret; } void init(int n) { memset(head+1,0xff,n*sizeof(int)); tot=0; } } int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { int s = 2*n+1,t = s+1; int sum = 0; maxflow::init(t); for(int i=1,d;i<=n;i++) { scanf("%d",&d); su***xflow::addedge(s,i,d); maxflow::addedge(i+n,t,d); } for(int i=0;i<m;i++) { int u,v; scanf("%d%d",&u,&v); maxflow::addedge(u,v+n,1); maxflow::addedge(v,u+n,1); } if(sum==maxflow::maxflow(s,t,t)) puts("Yes"); else puts("No"); } return 0; }
J.Easy Integration
题意:
求
题解:
首先引入两个函数:
可以发现所求即为,因此根据。
可得最终答案为
证明:
首先给出贝塔函数的一个递推公式:
因此
又因为
再给出伽马函数的递推公式;
所以可得得证。
这题好像还可以用oeis查到,学到了,oeis
#include <bits/stdc++.h> using namespace std; const int mod = 998244353; const int maxn = 2e6 + 10; typedef long long ll; ll frac[maxn]; long long ksm(long long x, long long y) { long long ans = 1; while (y) { if (y & 1) ans = ans * x % mod; y >>= 1; x = x * x % mod; } return ans; } int main() { frac[0] = frac[1] = 1; for (int i = 2; i <= 2e6 + 5; i++) { frac[i] = frac[i - 1] * i % mod; } int n; while (cin >> n) { ll p = frac[n] * frac[n] % mod; ll q = frac[2 * n + 1]; ll ans = p * ksm(q, mod - 2) % mod; cout << ans << "\n"; } return 0; }