Codeforces Round #661 (Div. 3) A~E1
原作者为 DOEMsy@cnblogs, 本作品采用 CC 4.0 BY 进行许可,转载请注明出处。
上分场找回了点自信。
https://codeforces.com/contest/1399
A. Remove Smallest
题意
给定一个数组 \(a_1,a_2,...,a_n\) ,每次操作可以选取数组中两个相差不大于 \(1\) 的数字,删除其中一个。
询问能否删到最后剩余一个数字。
解题
保证数组有序后,相邻元素差值不超过 \(1\) 即可。
#include
//#include
#define ll long long
#define fr(i,n) for(ll i=0;i>t;
while(t--){
int n;cin>>n;
int a[55];
fr(i,n) cin>>a[i];
sort(a,a+n);
int p = 1;
fr(i,n-1){
if(a[i+1]-a[i]>1) p++;
}
//cout<<1<1) cout<<"NO"<
B. Gifts Fixing
题意
有 \(n\) 个礼物,每个礼物由 \(a_i\) 个糖果和 \(b_i\) 个橘子组成。
每次可以对一个礼物进行以下操作:
- 吃掉一个糖果;
- 吃掉一个橘子;
- 吃掉一个糖果和一个橘子。
询问最少多少次操作可以使得所有礼物的糖果和橘子都相同。
解题
简单贪心,将所有的糖果和橘子变成分别变成最少的数量,每一堆的最少花费为 \(max(a_i-a_{min},b_i-b_{min})\) ,求和即可。
#include
//#include
#define ll long long
#define fr(i,n) for(ll i=0;i>t;
while(t--){
ll a[55],b[55];
int n;cin>>n;
fr(i,n) cin>>a[i];
fr(i,n) cin>>b[i];
ll mina = *min_element(a,a+n);
ll minb = *min_element(b,b+n);
ll ans = 0;
fr(i,n){
ans+=max(a[i]-mina,b[i]-minb);
}
cout<
C. Boats Competition
题面
有一群人,战斗力为 \(w_1,w_2,...,w_n\) ,要求两两组队,且每个队伍的战斗力必须相同。
询问最多可以组成多少个队伍。
解题
由于人数和战斗力范围较小,可以直接在 \([2,2n]\) 内遍历队伍战斗力范围,取组成数量最多值,复杂度 \(O(2n^2)\)。
#include
//#include
#define ll long long
#define fr(i,n) for(ll i=0;i>t;
while(t--){
int n;cin>>n;
int a[550];memset0(a);
fr(i,n){
int inp;cin>>inp;
a[inp]++;
}
int maxs = 0,maxnum = 0;
frr(s,2,2*n+1){
int num = 0;
frr(i,1,n+1){
if(s-i>0) num+=min(a[i],a[s-i]);
}
num/=2;
maxnum = max(num,maxnum);
}
cout<
D. Binary String To Subsequences
题意
给定一个由 \(01\) 组成的字符串 \(S\),问最少分成多少个子串,使得每个子串 \(01\) 交错。
并且输出每一个字符被分配到的子串编号。
解题
注意题中说的是子串,不是连续子串。
类似 \(dp\) 的思路,将前面的所有子串状态存储,对于当前字符 \(S_i\) 有:
- 如果存在结尾与 \(S_i\) 不同的子串,则可以用 \(S_i\) 续接该子串。
- 否者用 \(S_i\) 新建一个子串。
关于子串的存储只需要记录结尾和编号即可,复杂度 \(O(n)\) 。
#include
//#include
#define ll long long
#define fr(i,n) for(ll i=0;i>t;
while(t--){
int n;cin>>n;
string inp;cin>>inp;
int maxsz = 0;
//fr(i,n) a[i] = 0;
stackct[2];
fr(i,n){
if(!ct[!(inp[i]-'0')].empty()){
a[i] = ct[!(inp[i]-'0')].top();
ct[!(inp[i]-'0')].pop();
ct[(inp[i]-'0')].push(a[i]);
}else{
ct[(inp[i]-'0')].push(++maxsz);
a[i] = maxsz;
}
}
cout<
E1. Weights Division (easy version)
题意
给定一棵带边权的树,每次操作可以选择一条边,使其权值 \(w_i = \lfloor\frac{w_i}{2}\rfloor\) 。
询问至少多少次操作,可以使得叶节点到根部的路径权总和小于等于 \(S\) 。
解题
贪心,选取一个边会对权值和产生的影响为 \((w_i-\lfloor\frac{w_i}{2}\rfloor)*cnt\) ,\(cnt\) 为经过该边的叶节点数量,优先选取影响大的。
先 \(dfs\) 跑出每一条边的 \(w_i,cnt\),再以单调队列维护,不断操作,直到和小于等于 \(S\) 。
复杂度 \(O(nlogn)\) 。
#include
//#include
#define ll long long
#define fr(i,n) for(ll i=0;i=j;i--)
#define frrs(i,j,n,flag) for(ll i=j;i=j&&flag;i--)
#define yes "yes"
#define no "no"
#define memset0(dp) memset(dp,0,sizeof(dp))
#define min_get(a,b) a = min(a,b)
#define max_get(a,b) a = max(a,b)
#define PI 3.14159265354
#define print_arr(begin,end) for(auto it = begin;it!=end;it++) cout<<*it<<" "; cout<>a;return a;}
string to_str(double a) {stringstream ss;ss<inline void read(T &x){T f=1;x=0;char c=getchar();while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}x*=f;}
mapeg[1*e5];
struct cst_cnt{
ll cst,cnt;
cst_cnt(ll cs,ll cn){cst = cs,cnt = cn;}
bool operator < (const cst_cnt o)const{
return (cst-cst/2)*cnt<(o.cst-o.cst/2)*o.cnt;
}
};
priority_queuecost;
ll sum;
ll dfs(int i,int last){
ll cst;
if(i==last) cst = 0;
else cst = eg[i][last];
ll cnt = 0;
if(eg[i].size()==1&&i!=last) cnt = 1;
else{
for(auto it:eg[i]){
if(it.first!=last){
cnt += dfs(it.first,i);
}
}
}
sum += cnt*cst;
cost.push(cst_cnt(cst,cnt));
return cnt;
}
int main(){
int t;cin>>t;
while(t--){
ll n,s;cin>>n>>s;
frr(i,1,n+1) eg[i].clear();
ll v,u,w;
#define p(a,b) pair(a,b)
fr(i,n-1){
cin>>v>>u>>w;
eg[v].insert(p(u,w));
eg[u].insert(p(v,w));
}
while(!cost.empty()) cost.pop();
sum = 0;
dfs(1,1);
ll ans = 0;
while(sum>s){
cst_cnt tmp = cost.top();
sum -= (tmp.cst-tmp.cst/2)*tmp.cnt;
tmp.cst/=2;
cost.pop();
cost.push(tmp);
ans++;
}
cout<