2020牛客暑期多校训练营(第一场)解题报告

F

签到题。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=2e5+7;

string s1,s2;

int main(){
	while(cin>>s1>>s2){
		if(s1+s2==s2+s1) printf("=
");
		if(s1+s2<s2+s1) printf("<
");
		if(s1+s2>s2+s1) printf(">
");
	}
}

J

数学推导题。

[int_{0}^{1}x^n(1-x)^n = frac{1}{n+1}(1-x)^ndx^n+1 \ = frac{1}{n+1}(x^{n+1}(1-x)^n)igg|^{1}_{0}+nint_0^1x^{n+1}(1-x)^{n-1}dx\ =frac{n}{n+1}int_0^1x^{n+1}(1-x)^{n-1}dx \ int_0^1x^{n+1}(1-x)^{n-1}dx=frac{1}{n+2}(1-x)^{n-1}igg|_0^1+(n+1)int_0^1x^{n+2}(1-x)^{n-2}dx\ int_0^1x^n(1-x)^{n-1}dx=frac{n}{n+1}int_0^1x^{n+1}(1-x)^{n-1}dx \ int_0^1x^{n+1}(1+x)^{n-1}dx=frac{n-1}{n+2}int_0^1x^{n+2}(1-x)^{n-2}dx\ vdots\ int ^1_0x^{n+1}(1-x)^{n-1}dx=frac{1}{2n}int_0^1 x^{n+2}(1-x)^{n-2}dx\ int_0^1x^{2n}dx=frac{1}{2n+1}\ int^1_0x^n(1-x)^ndx=frac{n}{n+1} imesfrac{n-1}{n+2} imesfrac{n-2}{n+3}cdotsfrac{1}{2n} imesfrac{1}{2n+1} ]

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const ll mod=998244353;
const int N=2e6+7;

ll powmod(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

ll dp[N];

int main(){
	dp[0]=dp[1]=1;
	for(int i=2;i<N;i++) dp[i]=dp[i-1]*i%mod;
	int n;
	while(~scanf("%d",&n)){
		ll tmp=dp[2*n+1]*powmod(dp[n],mod-2)%mod;
		ll Ans=dp[n]*powmod(tmp,mod-2)%mod;

		printf("%lld
",Ans);
	}
}

H

最小费用最大流。本题的关键在于如何分配分数流量使得能让1流量的流流到汇点。首先我们发现图中每一条边的最大流量是相等的,所以每条边能贡献的流量相同,那么问题可以转换为选取最小的若干的增广路,使得最大流量为1的情况下,费用最小。我们可以默认每条边的流量为1,然后跑MCMF算法,存下每一条增广路的花费,按照增广路的花费从小到大排序,每次询问从小到达取增广路,直到流量为1,答案也可以一并算出来。如果取完所有的增广路,流量还未为1,则输出"NaN"。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x; 
}

const int N=5007;
const ll INF=0x3f3f3f3f3f3f3f;
#define PII pair <ll,ll> 
#define fr first
#define sc second
#define mp make_pair

struct MinCostFlow{
	vector<PII> ld;
	struct edge{
		ll v,f,c,r;
		edge(ll _v, ll _f, ll _c, ll _r) :v(_v), f(_f), c(_c), r(_r) {}
	};
	ll V=0,h[N],dis[N],pv[N],pe[N];
	vector <edge> G[N];

	inline void init(ll n){
		for(ll i=0;i<=V;i++) G[i].clear();
		V=n;
		ld.clear();
	}

	inline void Ins(ll u,ll v,ll f,ll c){
		G[u].push_back(edge(v,f,c,G[v].size() ));
		G[v].push_back(edge(u,0,-c,G[u].size()-1 ));
	}

	PII MCMF(ll s,ll t,ll Flow){
		ll cost=0,flow=0,newflow;fill(h,h+1+V,0);
		while(Flow){
			priority_queue <PII,vector<PII>,greater<PII> > Q;
			fill(dis,dis+V+1,INF);
			dis[s]=0,Q.push(mp(0,s));
			while(!Q.empty()){
				PII now=Q.top();Q.pop();
				ll u=now.sc;
				if(dis[u]<now.fr) continue;
				for(ll i=0;i<G[u].size();i++){
					edge &e=G[u][i];
					if(e.f>0&&dis[e.v]>dis[u]+e.c+h[u]-h[e.v]){
						dis[e.v]=dis[u]+e.c+h[u]-h[e.v];
						pv[e.v]=u,pe[e.v]=i;
						Q.push(PII(dis[e.v],e.v));
					}
				}
			}
			if(dis[t]==INF) break;
			for(ll i=0;i<=V;i++) h[i]+=dis[i];
			newflow=Flow;
			for(ll x=t;x!=s;x=pv[x]) newflow=min(newflow,G[pv[x]][pe[x]].f);
			Flow-=newflow,flow+=newflow,cost+=newflow*h[t];
			ld.push_back(mp(newflow*h[t],newflow));
			for(ll x=t;x!=s;x=pv[x]){
				edge &e=G[pv[x]][pe[x]];
				e.f-=newflow;
				G[x][e.r].f+=newflow;
			}
		}
		return mp(flow,cost);
	}
}A;

int main(){
	ll n,m;
	while(~scanf("%lld%lld",&n,&m)){
		ll s=1,t=n;
		A.init(n);
		for(ll i=1;i<=m;i++){
			ll u=input(),v=input(),c=input();
			A.Ins(u,v,1,c);
		}
		PII Ans=A.MCMF(s,t,INF);
		sort(A.ld.begin(),A.ld.end());
		ll q=input();
		while(q--){
			ll u=input(),v=input();
			ll fm=0,fz=v,ct=0;
			for(auto t:A.ld){
				if(ct+u<=v){
					fm+=t.fr*u;
					ct+=u;
				}else if(ct<v){
					fm+=(v-ct)*t.fr;
					ct+=(v-ct);
				}else break;
			}
			if(ct==v){
				ll gd=__gcd(fm,fz);
				printf("%lld/%lld
",fm/gd,fz/gd);
			}else printf("NaN
");
		}
	}
}

I

带花树开花算法。

最大流错误做法

被我用最大流水过去了。最大流在判奇环时会出现错误。

测试数据:

3 3
1 1 1
1 2
2 3
3 1
No

最大流建模就是每一个点拆成出点和入点,入点于源点相连,流量为该点的(d[i])值,出点于汇点相连,流量为该点的(d[i])值,对于一个入点与一个出点之间有一条流量为1的边的条件是在图上与之相连的。对于这个网络跑最大流,如果最大流等于(d[])数组之和,则是"Yes",否则为"No"。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=200;
const int M=4007;
const int INF=0x3f3f3f3f;
#define clr(a,b) memset(a,b,sizeof(a))

int n,m,s,t;
int cnt=0;
int head[N];
int dis[N],q[N];

int d[N];
vector <int> G[N];

struct edge{
    int v,w,next;
}e[M];

void Ins(int u,int v,int w){
    e[cnt]=(edge){v,w,head[u]};head[u]=cnt++;
    e[cnt]=(edge){u,0,head[v]};head[v]=cnt++;
}

int MkLevel(){
    int x,l=0,r=0;
    clr(dis,0);
    dis[s]=1,q[r++]=s;
    while(l<r){
        x=q[l++];
        for(int i=head[x];i!=-1;i=e[i].next){
            if(e[i].w&&!dis[e[i].v]){
                dis[e[i].v]=dis[x]+1;
                if(e[i].v==t) return 1;
                q[r++]=e[i].v;
            }
        }
    }
    return 0;
}

int extend(int s,int Lim){
    if(s==t) return Lim;
    int tmp,cost=0;
    for(int i=head[s];i!=-1;i=e[i].next){
        if(e[i].w&&dis[s]==dis[e[i].v]-1){
            tmp=extend(e[i].v,min(Lim-cost,e[i].w));
            if(tmp>0){
                e[i].w-=tmp,e[i^1].w+=tmp,cost+=tmp;
                if(Lim==cost) break;
            }else dis[e[i].v]=-1;
        }
    }
    return cost;
}

int Dinic(){
    int ans=0;
    while(MkLevel()) ans+=extend(s,INF);
    return ans;
}

int main(){
    while(~scanf("%d%d",&n,&m)){
    	cnt=0;
    	clr(head,-1);
    	int sum=0;
    	for(int i=1;i<=n;i++) d[i]=input(),G[i].clear(),sum+=d[i];

    	for(int i=1;i<=m;i++){
    		int u=input(),v=input();
    		G[u].push_back(v);
    		G[v].push_back(u);
    	}

    	s=0,t=2*n+1;
    	// cout<<s<<" "<<t<<endl;
    	for(int i=1;i<=n;i++){
    		Ins(s,i,d[i]);
    		// cout<<s<<" "<<i<<" "<<d[i]<<endl;
    		Ins(n+i,t,d[i]);
    		// cout<<n+i<<" "<<t<<" "<<d[i]<<endl;
    		for(auto v:G[i]){
    			Ins(i,n+v,1);
    			// cout<<i<<" "<<n+v<<" "<<1<<endl;
    		}
    	}

    	printf("%s
",Dinic()==sum?"Yes":"No");
    }
}

开花树正确做法

待补


A

待补

原文地址:https://www.cnblogs.com/-aether/p/13290703.html