2020牛客暑期多校训练营(第一场)
根据难度排序。
个人总结向。
如果有什么讲的不清楚的欢迎留言私信交流~
F. Infinite String Comparision(字符串+结论)
题意: 给出两个无限循环串的循环节,比较两个串的大小。
思路:
假设一个字符串 S 有循环节(不需要是完整循环节) p 和 q ,并且满足 ,那么 也是一个循环节。
题解中说到只要复制到长度为(n+m)即可,也就是说可以记住这个结论。
代码:
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define ll long long #define fi first #define se second #define inf 0x3f3f3f3f #define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; const int manx=1e6+5; const int N=1e3+5; const int mod=1e9+7; int main() { string a,b; while(cin>>a>>b){ string s1=a+b,s2=b+a; if(s1<s2) cout<<"<"<<endl; else if(s1==s2) cout<<"="<<endl; else cout<<">"<<endl; } return 0; }
J. Easy Integration(欧拉积分)
题意: 求 输出分数为
思路:
欧拉积分:
伽玛函数:
贝塔函数:
过程:
代码:
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define ll long long #define fi first #define se second #define inf 0x3f3f3f3f #define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; const int manx=2e6+5; const int N=1e3+5; const int mod=998244353; ll f[manx],n; ll qp(ll a, ll b){ ll ans=1; while(b){ if(b&1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans%mod; } ll inv(ll x){ return qp(x,mod-2); } int main() { io; f[1]=1; for(int i=2;i<=2000005;i++) f[i]=f[i-1]*i%mod; while(cin>>n){ ll ans=f[n]*f[n]%mod*inv(f[2*n+1])%mod; cout<<ans<<endl; } return 0; }
H. Minimum-cost Flow(最小费用最大流)
题意: 给定网络的边和费用,每次询问给定所有边的容量u和要求的流量v,问把一单位流量从1 流向 n 最少所需要的费用,如果不能满流输出NaN。
思路:
很明显每条边的容量都是相同的,所以我们可以初始化为1,这样做的好处是 就是当前询问的最大流,在增广路径的时候用 map 记录每一条路径的流量与花费(map会自动排序)。
如果 的话,则输出NaN 。
否则的话,我们不断地加入每一条路径,并且累加费用,直到流量不小于 v 时跳出循环,最后总的费用输出时分子分母同时除以分子分母的GCD即可。
代码:
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define ll long long #define fi first #define se second #define inf 0x3f3f3f3f #define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; const int manx=2e3+5; const int N=1e3+5; const int mod=1e9+7; bool vis[manx]; ll n,m,s,t,x,y,z,f,maxflow,mincost,cnt; ll dis[manx],pre[manx],last[manx],flow[manx],head[manx]; map<ll,ll>mps; struct node{ ll v,next,flow,dis; }a[manx]; inline void add(ll u,ll v,ll flow,ll dis){ a[++cnt].next=head[u],a[cnt].v=v,a[cnt].flow=flow,a[cnt].dis=dis,head[u]=cnt; } bool spfa(ll s,ll t) { for(int i=1;i<=n;i++) vis[i]=0,dis[i]=flow[i]=inf; queue<ll>q; q.push(s); vis[s]=1; dis[s]=0; pre[t]=-1; while(!q.empty()){ ll now=q.front(); q.pop(); vis[now]=0; for(int i=head[now];i!=-1;i=a[i].next){ if(a[i].flow>0&&dis[a[i].v]>dis[now]+a[i].dis){ dis[a[i].v]=dis[now]+a[i].dis; pre[a[i].v]=now; last[a[i].v]=i; flow[a[i].v]=min(flow[now],a[i].flow); if(!vis[a[i].v]){ vis[a[i].v]=1; q.push(a[i].v); } } } } return pre[t]!=-1; } inline void donic() { while(spfa(s,t)){ ll now=t; maxflow+=flow[t]; mincost+=flow[t]*dis[t]; mps[flow[t]*dis[t]]+=flow[t]; while(now!=s){ a[last[now]].flow-=flow[t]; a[last[now]^1].flow+=flow[t]; now=pre[now]; } } } inline void init(){ for(int i=1;i<=n;i++) head[i]=-1,last[i]=0; cnt=-1; maxflow=mincost=0; s=1,t=n; mps.clear(); } int main() { while(scanf("%lld%lld",&n,&m)!=EOF){ init(); for(int i=1;i<=m;i++){ scanf("%lld%lld%lld",&x,&y,&f); add(x,y,1,f); add(y,x,0,-f); } donic(); ll q; cin>>q; while(q--){ ll u,v; scanf("%lld%lld",&u,&v); if(maxflow*u<v) printf("NaN\n"); else{ x=0,y=v; for(auto sb: mps){ if(v>sb.se*u) v-=sb.se*u,x+=sb.fi*u; else { x+=sb.fi/sb.se*v; break; } } z=__gcd(x,y); printf("%lld/%lld\n",x/z,y/z); } } } return 0; }
I. 1 or 2(带花树)
题意: 给出一张图和每个点的期望度数,要求你删掉一些边使得每个点的度数都等于期望度数,问是否可行。
思路:
在这之前只知道匈牙利算法,现在了解到了一般图最大匹配的解决算法。
以知道这个算法为前提,难点在于想到拆点拆边。(当时我就不知道)
对于每个点要求的度数 ,我们将点 i 拆成个
对于每条边,我们将拆成,x,y,x与分别与 u 拆成的 个点连边,y点同理,最后再将连一条边(这样做是可以防止 u 或者 v 要求的度数为0的情况,此时直接在 x 与 y 之间连一条边就可以解决)
例如,题目中第三组样例:
3 2
1 1 2
1 3
2 3
第1个点拆成1个点,编号为1。
第2个点拆成1个点,编号为2。
第3个点拆成2个点,编号分别为3,4
第1条边拆成两个点,编号为5,6:(5,6)(5,1)(6,3)(6,4)连边。
第2条边拆成两个点,编号为7,8:(7,8)(7,2)(8,3)(8,4)连边。
注意在这题里是无向边。
代码:
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define ll long long #define fi first #define se second #define inf 0x3f3f3f3f #define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; const int manx=2e3+5; const int N=1e3+5; const int mod=1e9+7; deque<ll>q; bool g[N][N],inque[N],inblossom[N]; ll match[N],pre[N],base[N]; inline ll findancestor(ll u,ll v){ //找公共祖先 bool inpath[N]={false}; while(1){ u=base[u]; inpath[u]=true; if(match[u]==-1)break; u=pre[match[u]]; } while(1){ v=base[v]; if(inpath[v])return v; v=pre[match[v]]; } } inline void reset(ll u,ll anc){ //压缩花 while(u!=anc) { ll v=match[u]; inblossom[base[u]]=1; inblossom[base[v]]=1; v=pre[v]; if(base[v]!=anc)pre[v]=match[u]; u=v; } } inline void contract(ll u,ll v,ll n){ ll anc=findancestor(u,v); memset(inblossom,0,sizeof(inblossom)); reset(u,anc); reset(v,anc); if(base[u]!=anc)pre[u]=v; if(base[v]!=anc)pre[v]=u; for(int i=1;i<=n;i++) if(inblossom[base[i]]){ base[i]=anc; if(!inque[i]) q.push_back(i),inque[i]=1; } } inline bool dfs(ll S,ll n) { for(int i=0;i<=n;i++) pre[i]=-1,inque[i]=0,base[i]=i; q.clear(); q.push_back(S); inque[S]=1; while(!q.empty()){ ll u=q.front(); q.pop_front(); for(int v=1;v<=n;v++){ if(g[u][v]&&base[v]!=base[u]&&match[u]!=v){ if(v==S||(match[v]!=-1&&pre[match[v]]!=-1)) contract(u,v,n); else if(pre[v]==-1){ pre[v]=u; if(match[v]!=-1) q.push_back(match[v]),inque[match[v]]=1; else{ u=v; while(u!=-1){ v=pre[u]; ll w=match[v]; match[u]=v; match[v]=u; u=w; } return true; } } } } } return false; } inline int solve(ll n){ memset(match,-1,sizeof(match)); ll ans=0; for(int i=1; i<=n; i++) if(match[i]==-1&&dfs(i,n)) ans++; return ans; } //以上是带花树模板 //g[i][j]存放关系图:i,j是否有边,match[i]存放i所匹配的点 //建图开始初始化g //最终匹配方案为match //复杂度O(n^3) //点是从1到n的 //最终带花树solve给出的是匹配对数 ll d[60]; ll x[210],y[210]; ll mps[55][210]; inline bool build(ll n,ll m) { ll num=0; //拆点,给i点的第j个度一个标号,方便接下来的建图 for(int i=1;i<=n;i++) for(int j=1;j<=d[i];j++) mps[i][j]=++num; //拆边,把边被拆成的两个点分别与两头的每个度相连 for(int i=1;i<=m;i++){ for(int j=1;j<=d[x[i]];j++) g[mps[x[i]][j]][num+1]=g[num+1][mps[x[i]][j]]=1; for(int j=1;j<=d[y[i]];j++) g[mps[y[i]][j]][num+2]=g[num+2][mps[y[i]][j]]=1; g[num+1][num+2]=g[num+2][num+1]=1; num+=2; } if(solve(num)*2==num) return 1; return 0; } int main() { ll n,m; while(scanf("%lld%lld",&n,&m)!=EOF){ memset(g,0,sizeof(g)); for(int i=1;i<=n;i++) scanf("%lld",&d[i]); for(int i=1;i<=m;i++) scanf("%lld%lld",&x[i],&y[i]); if(build(n,m)) puts("Yes"); else puts("No"); } return 0; }