Codeforces Round #734 (Div. 3)

Codeforces Round #734 (Div. 3)

A. Polycarp and Coins

解释

假设两个的数量相同(x),则得到的总价值为 3x,若n % 3x != 0 ,那么余1可以给第一个多加一个,余二给第二个多加一个。

代码

int tt; cin>>tt;
    while(tt --){
    	int n; cin>>n;
    	int a = n/3,b = n/3;
    	if(n % 3 != 0){
    		if(n % 3 == 1) a ++;
    		else b ++;
		}
		cout<<a<<" "<<b<<endl;
	}

B1. Wonderful Coloring - 1

解释

每个字母的贡献为min(size(x),2) 。 最后答案/2。

代码

int tt; cin>>tt;
    while(tt --){
    	map<char,int> mp;
    	string s; cin>>s;
    	int res = 0;
    	for(int i=0;i<(int)s.size();i++){
    		mp[s[i]] ++;
    		if(mp[s[i]] <= 2) res ++;
		}
		cout<<(res/2)<<endl;
	}

B2. Wonderful Coloring - 2

解释

每个元素的贡献为min(size(x),k),存下下标,留下k的倍数个元素下表,然后按下表对应的元素从小到大排序,每一个赋值为i % k + 1避免同一个元素重复给一个颜色。

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;

int a[N];

signed main(){
	IOS
    int tt; cin>>tt;
    while(tt --){
    	int n,k; cin>>n>>k;
    	map<int,int> mp;
    	vector<int> ans,res(n+1,0);
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=1;i<=n;i++){
    		mp[a[i]] ++;
    		if(mp[a[i]] <= k) ans.push_back(i);
		}
		while((int)ans.size() % k != 0) ans.pop_back();
		sort(ans.begin(),ans.end(),[](int i,int j){
			return a[i] < a[j];
		});
		for(int i=0;i<(int)ans.size();i++) res[ans[i]] = i % k + 1;
		for(int i=1;i<(int)res.size();i++) cout<<res[i]<<" ";
		cout<<endl;
	}
    
	return 0;
}

C. Interesting Story

解释

贪心,分别计算5个字母,统计5个字母在每一个字符串里面的个数。

假设在一个字符串里面字母x的个数为a,其它字母长度为b,如何选取更优?贪心方法,尽量选择a-b最大的,这样每次带来的负贡献会最小。

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;

struct Node{
	int x,y;
	bool operator < (const Node&T)const{
		return (x - T.x) > (y - T.y);
	}
};

vector<Node> a[6];

signed main(){
	IOS
	int tt; cin>>tt;
	while(tt --){
		for(int i=0;i<5;i++) a[i].clear();
		int n; cin>>n;
		for(int i=1;i<=n;i++){
			string s; cin>>s;
			int t[] = {0,0,0,0,0};
			int len = (int)s.size();
			for(int j=0;j<len;j++) t[s[j]-'a'] ++;
			for(int j=0;j<5;j++) a[j].push_back({t[j],len-t[j]});
		}
		int ans = 0;
		for(int i=0;i<5;i++) sort(a[i].begin(),a[i].end());
		for(int i=0;i<5;i++){
			int res = 0,cnt = 0;
			for(int j=0;j<n;j++){
				res += (a[i][j].x - a[i][j].y);
				if(res <= 0) break;
				cnt ++;
			}
			ans = max(cnt,ans);
		}
		cout<<ans<<endl;
	}
	return 0;
}

D1. Domino (easy version)

解释

考虑nm奇偶性。

  1. 当n为奇数,一定是用水平的去把最下层铺满,然后n就相当于n-1,变成偶数处理。m此时不可能为奇数(题意),只需要判断 2k 是否大于m就行,然后去掉填充的这部分。
  2. 把剩下的k变成4x4的矩形填充。
  3. 判断上述在形成过程中是否合法。

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
#define int long long

signed main(){
	IOS
    int tt; cin>>tt;
	while(tt --){
		int n,m,k; cin>>n>>m>>k;
		k *= 2;
		int n1 = n,m1 = m;
		if(n % 2){
			if(k < m){
				cout<<"NO"<<endl;
				continue;
			}
			k -= m;
			n --;
		}
		if(k % 4 != 0){
			cout<<"NO"<<endl;
			continue;
		}
		k /= 4;
		if(m % 2) m --;
		int res = n * m / 4;
		if(k > res){
			cout<<"NO"<<endl;
			continue;
		}
		cout<<"YES"<<endl;
	} 

	return 0;
}

D2. Domino (hard version)

解释

暴力填数,行构成 4 × 4 4\times 4 4×4的矩阵后可以用ab交替填完,列暴力填与周围不同的字母。奇数填的最后一行用x,y字母填

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e2 + 10;

char ans[N][N];

char slove(int i,int j,int m){
	map<char,int> mp;
	if(i > 0) mp[ans[i-1][j]] = 1;
	if(j > 0) mp[ans[i][j-1]] = 1,mp[ans[i+1][j-1]] = 1;
	mp[ans[i][j+1]] = 1,mp[ans[i][j+2]] = 1,mp[ans[i+1][j+1]] = 1;
	for(int i=0;i<26;i++) if(!mp[i+'a']) return (i+'a');
}

signed main(){
	IOS
    int tt; cin>>tt;
	while(tt --){
		for(int i=0;i<110;i++)
			for(int j=0;j<110;j++)
				ans[i][j] = '1';
		int n,m,k; cin>>n>>m>>k;
		k *= 2;
		int n1 = n,m1 = m;
		if(n % 2){
			if(k < m){
				cout<<"NO"<<endl;
				continue;
			}
			for(int i=0;i<m;i++) ans[n-1][i] = ((i/2)%2) == 0?'x':'y';
			k -= m;
			n --;
		}
		if(k % 4 != 0){
			cout<<"NO"<<endl;
			continue;
		}
		k /= 4;
		if(m % 2) m --;
		int res = n * m / 4;
		if(k > res){
			cout<<"NO"<<endl;
			continue;
		}
		cout<<"YES"<<endl;
		for(int i=0;i<n;i+=2)
			for(int j=0;j<m;j+=2){
				if(k <= 0) break;
				if((j/2) % 2){
					ans[i][j] = ans[i][j+1] = 'b';
					ans[i+1][j] = ans[i+1][j+1] = 'a';
				}else{
					ans[i][j] = ans[i][j+1] = 'a';
					ans[i+1][j] = ans[i+1][j+1] = 'b';
				}
				k --;
			}
		for(int i=0;i<n1;i++)
			for(int j=0;j<m1;j++){
				if(ans[i][j] == '1'){
					ans[i][j] = ans[i+1][j] = slove(i,j,m1);
				}
			}
		for(int i=0;i<n1;i++,cout<<endl)
			for(int j=0;j<m1;j++)
				cout<<ans[i][j];
	}

	return 0;
}

E. Fixed Points

解释

d p ( i , j ) dp(i,j) dp(i,j) 表示处理完前 i − 1 i-1 i1个,且当前正在处理第 i i i个,且有 j j j​个被删除。

转移方程

  1. 删除 i i i, d p ( i , j ) = d p ( i − 1 , j − 1 ) dp(i,j) = dp(i-1,j-1) dp(i,j)=dp(i1,j1)
  2. 不删除 i i i, d p ( i , j ) = d p ( i − 1 , j ) + ( a ( i ) = = ( i − j ) ? 1 : 0 ) dp(i,j) = dp(i-1,j) + (a(i) == (i-j)?1:0) dp(i,j)=dp(i1,j)+(a(i)==(ij)?1:0)

答案

d p ( n , i ) , i > = 0 , i < = n dp(n,i),i>=0,i<=n dp(n,i),i>=0,i<=n

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e3 + 10;

int dp[N][N],a[N];
signed main(){
    int tt; cin>>tt;
    while(tt --){
    	int n,k; cin>>n>>k;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<i;j++){
    			dp[i][j] = dp[i-1][j-1];
    			dp[i][j] = max(dp[i][j],dp[i-1][j] + ((a[i] == (i-j))?1ll:0ll));
			}
		int ans = -1;
		for(int i=0;i<=n;i++)
			if(dp[n][i] >= k){
				ans = i;
				break;
			}
		cout<<ans<<endl;
	}
	return 0;
}

F. Equidistant Vertices

解释

以任意一个节点为根节点,这个根节点中一些子树选择一个深度相同的点,则这些点两两之间的距离相同。

**状态表示:**设 d p ( d , i , j ) dp(d,i,j) dp(d,i,j)​​​表示以某一个节点为根节点时,前i个子树中选择了j个点,且j个点的深度都为d,所得到的方案数

状态转移:

  1. 当前子树不选择任何点 d p ( d , i , j ) = d p ( d , i − 1 , j ) dp(d,i,j) = dp(d,i-1,j) dp(d,i,j)=dp(d,i1,j)
  2. 当前子树选择一个点 d p ( d , i , j ) = d p ( d , i − 1 , j − 1 ) ∗ c n t ( i , d ) , c n t ( i , d ) dp(d,i,j) = dp(d,i-1,j-1)*cnt(i,d),cnt(i,d) dp(d,i,j)=dp(d,i1,j1)cnt(i,d),cnt(i,d)​ 表示第i棵子树节点深度为d的个数

对于每一个节点,都可以作为根节点。

步骤

  1. 枚举根节点
  2. 计算以此为根节点的所有子树各个深度节点的数量
  3. 进行状态转移

复杂度 O ( n 3 ) O(n^3) O(n3)

代码

#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e2 + 10;
const int mod = 1e9 + 7;

int n,k; 
int ne[N],h[N],e[N],idx=1;
int dp[N][N],cnt[N][N];

void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs(int u,int fa){
	cnt[u][0] = 1;
	for(int i=h[u];i;i=ne[i]){
		int j = e[i];
		if(j == fa) continue;
		dfs(j,u);
		for(int deep=1;deep<=n;deep++) cnt[u][deep] += cnt[j][deep-1];
	}
}

int slove(int u){
	for(int i=0;i<=n;i++)
		for(int j=0;j<=n;j++)
			cnt[i][j] = 0;
	dfs(u,-1); // 计算以u为根节点,子树深度为d的个数有多少个 
	
	vector<int> son; son.push_back(-1);
	for(int i=h[u];i;i=ne[i]) son.push_back(e[i]);
	
	int len = (int)son.size(),res = 0;

	for(int d=0;d<=n;d++){
		dp[0][0] = 1;
		for(int i=1;i<len;i++){
			dp[i][0] = 1;
			for(int j=1;j<=i;j++){
				dp[i][j] = (dp[i][j] + dp[i-1][j]) % mod;
				dp[i][j] = (dp[i][j] + (dp[i-1][j-1] * cnt[son[i]][d] % mod)) % mod;
			}
		}
		res = (res + dp[len-1][k]) % mod;
		for(int i=1;i<len;i++)
			for(int j=1;j<=i;j++)
				dp[i][j] = 0;
	}
	
	return res;
}

signed main(){
	IOS
    int tt; cin>>tt;
    while(tt --){
    	for(int i=1;i<=idx;i++) h[i] = 0;
		idx = 1;
    	cin>>n>>k;
    	for(int i=1;i<n;i++){
    		int a,b; cin>>a>>b;
    		add(a,b);add(b,a);
		}
		int ans = 0;
		if(k == 2) ans = (n * (n - 1) / 2) % mod;
		else for(int root=1;root<=n;root++) ans = (ans + slove(root)) % mod;
		cout<<ans<<endl;
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值