2020 Multi-University Training Contest 2 解题报告

1001


并查集。考虑按点权大到小把点插入图中,每次插入点到图中,我们不妨设集合(S)(其中(s_i)为第(i)个集合的权值)为与当前点相连的连通块组成的集合,(w)为当前节点的权值。易得插入一个点得到的新连通块的公式为:

[sum^S (s_i-w)+w ]

那么维护集合我们很自然的想到用并查集维护。最后扫一遍每个点,保证每个连通块都加入答案。

#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;
}

#define PII pair <int,int>
#define fr first
#define sc second
#define mp make_pair

const int N=1e5+7;

int n,m;
ll Ans=0;

vector <int> G[N];
PII a[N];
ll vis[N],ans[N];

int fa[N],rk[N],vs[N];
int find(int x){ return fa[x]==x? x:(fa[x]=find(fa[x]));}
void merge(int x,int y){
    x=find(x);y=find(y);
    x==y? 0:rk[x]>=rk[y]? fa[y]=x,rk[x]+=rk[y]:fa[x]=y,rk[y]+=rk[x];
}

int main(){
    int T=input();
    while(T--){
        n=input(),m=input();

        int sta=1;Ans=0;
        for(int i=1;i<=n;i++){
            int val=input();
            a[i]=mp(val,i);
            fa[i]=i,rk[i]=1,G[i].clear();
            vis[i]=0,ans[i]=0;vs[i]=0;
        }

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

        sort(a+1,a+1+n);

        for(int i=n;i>=1;i--){
            int u=a[i].sc,val=a[i].fr;
            int cnt=0;
            for(auto v:G[u]){
                if(vis[v]){
                    if(find(u)!=find(v)) ans[u]+=(ans[find(v)]-val);
                    merge(u,v);
                }
            }
            vis[u]=1;
            ans[find(u)]=ans[u]+val;
        }

        ll res=0;
        for(int i=1;i<=n;++i){
            if(vs[find(i)]==0) res+=ans[find(i)],vs[find(i)]=1;
        }
        printf("%lld
",res);
    }
}

1012

比赛的时候dp没推出来,就是菜☹。我们观察到B串非常短(m)最大只有20,我们考虑把复杂度尽量与询问的区间长度无关,与B串的长度有关。我们这个时候想到了一个非常好用的结构序列自动机,序列自动机能够让我们在原串上快速移动,这样我们的所有操作就与区间长度无关,而与(m)有关了。

现在问题转换为如何通过序列自动机,求区间的LCS,为啥要求LCS,因为我编辑距离只有增加和删除,那么最优一定是尽可能的保留更多的字符不被删除,那么我们就很自然的想到要求LCS,最后答案一定是在原串中要删掉多少个字符((r-l+1-LCS))加上要加入多少个字符使得答案匹配的数量(|B|-LCS),即((r-l+1-LCS)+|B|-LCS)。我们不妨记区间([l,r])的LCS为(LCS(l,r)),那么我们求(LCS(l,r)),需要考虑(dp[i][j])表示开始(B)的前(i)个位置匹配长度为(j)的LCS最远能到哪里。那么转移方程易得:

[dp[i][j]=min(dp[i][j],nxt[dp[i-1][j-1]][s[i]-'a']) ]

最后扫一遍(dp[m][i])求答案即可。

#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=1e5+7;

int nxt[N][27];

char a[N],b[N];
int lena,lenb;

void init(){
	memset(nxt,0,sizeof nxt);
	int len=strlen(a+1);
	for(int i=0;i<26;i++) nxt[len][i]=1e9;
	for(int i=len;i>=1;i--){
		for(int j=0;j<=25;j++) nxt[i-1][j]=nxt[i][j];
		nxt[i-1][a[i]-'a']=i;
	}
}

int dp[27][27];

ll getans(int l,int r){
	memset(dp,0x3f,sizeof dp);

	dp[0][0]=l-1;
	for(int i=1;i<=lenb;i++){
		dp[i][0]=l-1;
		for(int j=1;j<=i;j++){
			dp[i][j]=dp[i-1][j];
			if(dp[i-1][j-1]<=r){
				dp[i][j]=min(dp[i][j],nxt[dp[i-1][j-1]][b[i]-'a']);
			}
		}
	}
	
	int res=1e9;
	for(int i=0;i<=lenb;i++){
		if(dp[lenb][i]<=r) res=min(res,(r-l+1-i)+(lenb-i));
	}
	return res;
}


int main(){
	int T=input();

	while(T--){
		scanf("%s%s",a+1,b+1);
		init();
		lena=strlen(a+1),lenb=strlen(b+1);

		int Q=input();
		for(int i=1;i<=Q;i++){
			int l=input(),r=input();
			printf("%lld
",getans(l,r));
		}
	}
}

1006

感觉是温暖的签到?要你计算(A*B=C),然后某一位(1)被篡改位(0),那么显然枚举哪一位被篡改就行了,然后发现这个(A,B,C)数特别大,然后要考虑同余,为了避免题目出现误差,所以不能使fib的前(2*10^6)项同余。随便搞一手自然溢出,就过了。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define uint unsigned int
uint input(){
	uint 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=2e6+7;

uint f[N];

void init(){
	f[0]=1,f[1]=1;
	for(int i=2;i<N;i++) f[i]=f[i-1]+f[i-2];
}
int c[N];

int main(){
	init();
	int T=input();

	while(T--){
		uint A=0,B=0,C=0;
		int n,m,p,t;

		n=input();for(int i=1;i<=n;i++) A+=(f[i]*input());
		m=input();for(int i=1;i<=m;i++) B+=(f[i]*input());
		p=input();for(int i=1;i<=p;i++) c[i]=input(),C+=(f[i]*c[i]);

		uint tmp=A*B;

		for(int i=1;i<=p;i++){
			if(c[i]) continue;
			if(C+f[i]==tmp){
				printf("%d
",i);
				break;
			}
		}
	}
}

1010

温暖的签到题。搜索就完事了。

#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=57;

struct node{
	ll a,b,c,d;
};

vector<node> a[N],b[N];
int n,k;
ll Ans=0;

void dfs(int dep,ll A,ll B,ll C,ll D){
	if(dep>k){Ans=max((100+A)*(100+B)*(100+C)*(100+D),Ans);return;}
	for(int i=0;i<b[dep].size();i++){
		dfs(dep+1,A+b[dep][i].a,B+b[dep][i].b,C+b[dep][i].c,D+b[dep][i].d);
	}
}

int main(){
	int T=input();
	while(T--){
		n=input(),k=input();

		for(int i=1;i<=k;i++) a[i].clear();Ans=0;

		for(int i=1;i<=n;i++){
			int t=input(),_a=input(),_b=input(),_c=input(),_d=input();
			a[t].push_back((node){_a,_b,_c,_d});
		}
		int cnt=0;
		for(int i=1;i<=k;i++) if(a[i].size()) b[++cnt]=a[i];
		k=cnt;

		dfs(1,0,0,0,0);

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

1005

简单费用流。容易建模n个人跟m个机器连一条边,边流量为(1),费用为(a_i*j^2+b_i*j+c_i)。但是发现(m)太大了,直接建图根本存不下,我们需要缩减图的规模,我们贪心的想一条增广路的权值一定在二次函数最低点的附近,我们可以通过三分法简单的得到包括中点长为n的区间,为了保险我把边的区间扩大了5倍。然后由于我们取点是取一个区间的若干段,所以我们需要对取到的点进行离散化。然后就跑MCMF,保留每次求到增广路的花费即可。

#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=1007*1007;
const ll INF=0x3f3f3f3f3f3f3f;
#define PII pair <ll,ll> 
#define fr first
#define sc second
#define mp make_pair

#define dbg printf("PASS IN LINE %d
",__LINE__);

vector <ll> Ans;

struct MinCostFlow{
	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;	
	}

	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];
			Ans.push_back(cost);
			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;

struct node{
	ll a,b,c;
}wk[N];

ll n,m;

ll cal(ll a,ll b,ll c,ll j){
	return a*j*j+b*j+c;
}

PII getmin(ll a,ll b,ll c){
	ll l=1,r=1e8;
	while(r-l+1>=min(n,m)){
		ll mid1=(2*l+r)/3,mid2=(l+2*r)/3;
		if(cal(a,b,c,mid1)<cal(a,b,c,mid2)) r=mid2;
		else l=mid1;
	}
	return mp(l,r);
}

vector<ll> v;
map <PII,int> p;

int getid(ll x) {return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}

void work(){
	Ans.clear();v.clear();
	for(int i=1;i<=n;i++){
		PII tmp=getmin(wk[i].a,wk[i].b,wk[i].c);
		for(int j=max(tmp.fr-2*n,1ll);j<=min(tmp.sc+2*n,m);j++){
			v.push_back(j);
		}
	}
	sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end());

	A.init((n+v.size()+1));
	int cnt=0;
	for(int i=1;i<=n;i++){
		PII tmp=getmin(wk[i].a,wk[i].b,wk[i].c);
		for(int j=max(tmp.fr-2*n,1ll);j<=min(tmp.sc+2*n,m);j++){
			A.Ins(i,n+getid(j),1,cal(wk[i].a,wk[i].b,wk[i].c,j));
			p[mp(i,n+getid(j))]=1;
		}
	}
	
	

	int s=0,t=n+v.size()+1;

	for(int i=1;i<=n;i++){
		A.Ins(s,i,1,0);
	}

	for(int i=1;i<=v.size();i++){
		A.Ins(i+n,t,1,0);
	}

	PII tmp=A.MCMF(s,t,INF);

	sort(Ans.begin(),Ans.end());
	for(int i=0;i<Ans.size();i++){
		printf("%lld%c",Ans[i],i==Ans.size()-1? '
':' ');
	}
}

int main(){
	int T=input();

	while(T--){
		n=input(),m=input();

		for(int i=1;i<=n;i++){
			wk[i].a=input(),wk[i].b=input(),wk[i].c=input();
		}
		work();
	}
}
原文地址:https://www.cnblogs.com/-aether/p/13369518.html