Codeforces Round #635 (Div.1) 题解 (ABC)

比赛链接:https://codeforces.com/contest/1336

A. Linova and Kingdom

贪心

显然不会出现某个旅游城市的父亲是工业城市的情况(因为它俩交换一下就会有更大收益),因此最优解一定是工业城市远离根,旅游城市靠近根

在上述情况下尝试着把一个(父亲是旅游城市的)工业城市变成旅游城市(好绕啊),会发现happiness变化量等于这个结点为根的子树大小,减去这个结点的深度

因此让所有城市刚开始都是工业城市,把根放入优先队列中,然后每次取出happiness变化量最大的结点并将其儿子放入优先队列中

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
#define vector basic_string
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=200010; typedef long long ll;
#define int ll
int sz[N],dep[N];
bool vis[N];
vector<int> a[N];
void dfs(int x){
	vis[x]=1;
	sz[x]=1;
	for(auto p:a[x])
	if(!vis[p]){
		dep[p]=dep[x]+1;
		dfs(p);
		sz[x]+=sz[p];
	}
}
int f(int x){
	return sz[x]-dep[x];
}
int n,k;
priority_queue<pair<ll,ll>> q;
signed main(){
	cin>>n>>k;
	repeat(i,0,n-1){
		int x,y; cin>>x>>y; x--,y--;
		a[x]+=y;
		a[y]+=x;
	}
	dep[0]=1;
	dfs(0);
	ll ans=0;
	q.push({f(0),0});
	fill(vis,vis+n,0);
	repeat(i,0,n-k){
		ans+=q.top().first;
		int x=q.top().second; q.pop(); vis[x]=1;
		for(auto p:a[x])
		if(!vis[p])
			q.push({f(p),p});
	}
	cout<<ans<<endl;
	return 0;
}

B. Xenia and Colorful Gems

目前有两种方法(群里大佬说的forforfor可能是第三种方法但是我没听懂)

首先对三个数组a,b,c排序,然后遍历数组a,在数组b中二分查找出最接近 \(a[i]\) 的两个数(一个大于等于,一个小于等于)\(b[j_1],b[j_2]\),然后在数组c中二分查找出最接近 \(\dfrac{a[i]+b[j_1]}2\) 的两个数 \(c[k_{11}],c[k_{12}]\),以及最接近 \(\dfrac{a[i]+b[j_2]}2\) 的两个数 \(c[k_{21}],c[k_{22}]\),更新一下答案(这样会更新4次)。最后,把数组b和c交换一下重复上述操作

第二种方法优雅很多,即排序后每个数组弄一个指针(指向数组头),考虑三个指针向后移动的三个答案(没有真的移动),哪个答案小就移动哪个(这才是真的移动),边跑边更新答案

两个方法都贴出来了

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=200010; typedef long long ll; const ll INF=~0ull>>2;
#define int ll
vector<int> a,b,c;
ll ans;
template<typename T> T sqr(const T &x){return x*x;}
void up(int x,int y,int z){
	ans=min(ans,sqr(x-y)+sqr(y-z)+sqr(z-x));
}
void work2(int A,int B,const vector<int> &c){
	int mid=(A+B)/2;
	int ci=lower_bound(c.begin(),c.end(),mid)-c.begin();
	if(ci!=(int)c.size())up(A,B,c[ci]);
	if(ci!=0)up(A,B,c[ci-1]);
}
void work(int A,const vector<int> &b,const vector<int> &c){
	int bi=lower_bound(b.begin(),b.end(),A)-b.begin();
	if(bi!=(int)b.size())work2(A,b[bi],c);
	if(bi!=0)work2(A,b[bi-1],c);
}
signed main(){
	int T; cin>>T;
	while(T--){
		ans=INF*2;
		{
			int n1,n2,n3,x; cin>>n1>>n2>>n3;
			a.clear(); b.clear(); c.clear();
			repeat(i,0,n1)cin>>x,a.push_back(x);
			repeat(i,0,n2)cin>>x,b.push_back(x);
			repeat(i,0,n3)cin>>x,c.push_back(x);
		}
		sort(a.begin(),a.end());
		sort(b.begin(),b.end());
		sort(c.begin(),c.end());
		repeat(i,0,a.size()){
			work(a[i],b,c);
			work(a[i],c,b);
		}
		cout<<ans<<endl;
	}
	return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=200010; typedef long long ll; const ll INF=~0ull>>2;
#define int ll
vector<int> a,b,c;
ll ans;
template<typename T> T sqr(const T &x){return x*x;}
int cal(int x,int y,int z){
	return sqr(x-y)+sqr(y-z)+sqr(z-x);
}
template<typename T>
int pcal(T x,T y,T z){
	return cal(*x,*y,*z);
}
signed main(){
	int T; cin>>T;
	while(T--){
		ans=INF*2;
		{
			int n1,n2,n3,x; cin>>n1>>n2>>n3;
			a.clear(); b.clear(); c.clear();
			repeat(i,0,n1)cin>>x,a.push_back(x);
			repeat(i,0,n2)cin>>x,b.push_back(x);
			repeat(i,0,n3)cin>>x,c.push_back(x);
		}
		sort(a.begin(),a.end());
		sort(b.begin(),b.end());
		sort(c.begin(),c.end());
		auto p1=a.begin(),p2=b.begin(),p3=c.begin();
		while(1){
			int now=INF*2; auto nowp=&p1; ans=min(ans,pcal(p1,p2,p3));
			if(p1!=a.end()-1 && pcal(p1+1,p2,p3)<now)now=pcal(p1+1,p2,p3),nowp=&p1;
			if(p2!=b.end()-1 && pcal(p1,p2+1,p3)<now)now=pcal(p1,p2+1,p3),nowp=&p2;
			if(p3!=c.end()-1 && pcal(p1,p2,p3+1)<now)now=pcal(p1,p2,p3+1),nowp=&p3;
			if(now==INF*2)break;
			(*nowp)++;
		}
		cout<<ans<<endl;
	}
	return 0;
}

C. Kaavi and Magic Spell

这个是区间dp,而且真的不难,不要被它的位置吓到了(我承认我被吓到了

首先把字符串 \(T\) 的尾部加上特殊字符让它的长度和 \(S\) 相等(比 \(S\) 还长更好,雾),然后考虑状态转移方程

如果我们已经生成了 \(T\) 的某个子串 \(t\),那么就可以把 \(S[len(t)]\) 插入 \(t\) 的前面或者后面,这时候要考虑是否匹配(即 \(T\)\(t\) 的前面或者后面是否等于 \(S[len(t)]\)),方案数也随之转移

因此 \(dp[l][r]\) 表示生成 \(T[l..r]\) 有多少方案数,就有 \(dp[l,r]=dp[l+1][r]\times[是否匹配]+dp[l][r-1]\times[是否匹配]\)(具体懒得写了,看代码吧)

可以自上而下的记忆化搜索,也可以自下而上的标准动规(比赛时我记忆化写炸了所以换成dp了)

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=3010; typedef long long ll;
const int mod=(0?1000000007:998244353);
#define int ll
string s,t;
int rec[N][N],n,final;
signed main(){
	cin>>s>>t; t+=string(s.size()-t.size()+1,'?');
	n=s.size();
	repeat(i,0,n+1)rec[i][i]=1;
	repeat(len,1,n+1){
		repeat(l,0,n)
		if(l+len<=n){
			int r=l+len; //注意注意注意这里区间是左闭右开的,即 T[l..(r-1)]
			ll ans=0;
			if(t[l]=='?' || s[len-1]==t[l])ans+=rec[l+1][r];
			if(t[r-1]=='?' || s[len-1]==t[r-1])ans+=rec[l][r-1];
			rec[l][r]=ans%mod;
		}
	}
	repeat(i,0,n+1)
	if(t[i]=='?')
		final=(final+rec[0][i])%mod;
	cout<<final<<endl;
	return 0;
}

E1想假了,等官方题解出来了再更新/kk

更:我能看懂E1的题解,但是代码根本写不来,那就不更了23333

posted @ 2020-04-16 14:55  axiomofchoice  阅读(...)  评论(...编辑  收藏