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奇偶性。
- 当n为奇数,一定是用水平的去把最下层铺满,然后n就相当于n-1,变成偶数处理。m此时不可能为奇数(题意),只需要判断 2k 是否大于m就行,然后去掉填充的这部分。
- 把剩下的k变成4x4的矩形填充。
- 判断上述在形成过程中是否合法。
代码
#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 i−1个,且当前正在处理第 i i i个,且有 j j j个被删除。
转移方程
- 删除 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(i−1,j−1)
- 不删除 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(i−1,j)+(a(i)==(i−j)?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,所得到的方案数
状态转移:
- 当前子树不选择任何点 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,i−1,j)
- 当前子树选择一个点 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,i−1,j−1)∗cnt(i,d),cnt(i,d) 表示第i棵子树节点深度为d的个数
对于每一个节点,都可以作为根节点。
步骤
- 枚举根节点
- 计算以此为根节点的所有子树各个深度节点的数量
- 进行状态转移
复杂度 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;
}