2020牛客第十场 C D I
比赛网址:https://ac.nowcoder.com/acm/contest/5675
C Decrement on the Tree
给一棵边权非负的树,规定一个操作(u,v)为将u到v路径上所有边边权减1,求让所有边为0至少需要多少次操作。
一开始可能觉得毫无思路,但是硬着头皮做下去,终于还是想出了解法。
不过我意识到了该学学set的用法了。set是不能处理相同的元素的,需要用mutiset,且删除时用S.erase(S.find(x));
D Hearthstone Battlegrounds
一道模拟题,由于重要的是经验而不是题目,就不放题面了。
这道题我一开始以为是O(1)的分类讨论,我相信只要分得足够细,是一定可以做到O(1)求出结果的。
但是分了几个小时后,还是放弃了。
看了题解,又回头看了数据范围,总共才10^6,模拟就完了......三分钟敲完代码AC......md
下次遇到困难,比如写了半小时还没做出来,不妨再仔细读下题目,或许看错题或是有另外方法了呢。
I Tournament
n个人两两比赛,每天一场,共n*(n+1)/2天。规定每个人停留的天数是他参加的第一场比赛到他参加的最后一场比赛。要求所有人停留时间之和最短,求比赛安排。
所有人的停留天数之和,等于每一天的停留人数之和。
而停留人数的变化是连续的,而且是单峰的。连续是指除了每天人数最多+2或-2;单峰是指人数必定先增加后减小,因为1个人想要离开,必须跟其他人比赛,这样必然有一个从0到n的不下降区间,而一个人离开后不可能再回来,这样必然有一个从n到0的不上升区间。
我们先从峰值考虑,如何让有n个人的天数最少。很简单,可以只有1天。
现在考虑如何让有至少n-1个人的天数最少。只要先让1号和2号与前n-2号人都比赛过,之后进行1:(n-1)、1:n、2:(n-1)、2:n这四场比赛就行了,一共4天。
同时可以看到,在保证至少有n-1个人的天数最少的前提下,可以实现有n个人的天数最少。
依此类推,我们得到了对于峰值的赛程安排:
.... 1 - (n-i+1)
...
|... 1 - (n-3)
|... 2 - (n-3)
|... 3 - (n-3)
||.. 1 - (n-2)
||.. 2 - (n-2)
|||. 1 - (n-1)
|||| 1 - n
|||. 2 - (n-1)
|||. 2 - n
||.. 3 - (n-2)
||.. 3 - (n-1)
||.. 3 - n
|... 4 - (n-3)
|... 4 - (n-2)
|... 4 - (n-1)
|... 4 - n
...
.... i - n
这里需要保证 i<(n-i+1),解得 i<(n+1)/2,我们令mid=n/2。
这说明对于1mid和(n-mid+1)n之间得比赛如何安排,我们已经知道了。现在的问题是1~mid和n-mid+1内部的比赛如何安排。
我们依然延续那个思路:让有至少3人的天数最少,让有至少4人的天数最少......
有3人的天数至少为n(n+1)/2-2。有4人的天数至少为n(n+1)/2-6......过程我想我不用再详写了吧
这样我们就得到了1mid和(n-mid+1)n内部的安排。但是如果n为奇数,mid与(n-mid+1)之间还有一个数:mid+1。
对mid+1号的安排依然遵循那个原则。
这样,最优的赛程安排就完成了。
全部代码:
C
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
const int N=1e5+10;
typedef long long LL;
struct Tri{
int a, b, c;
Tri(int a=0, int b=0, int c=0):a(a),b(b),c(c){}
};
vector<Tri> V;
set::multiset<LL> S[N];
LL sum[N], maxn[N];
int n, q;
LL cnt;
LL updata(int u){
return maxn[u]*2ll>sum[u]?(2ll*maxn[u]-sum[u]):(sum[u]&1ll);
}
int main(){
cin>>n>>q;
for(int i=1, u, v, w; i<n; ++i){
scanf("%d%d%d", &u, &v, &w);
V.push_back(Tri(u,v,w));
sum[u]+=w; sum[v]+=w;
S[u].insert(-w); S[v].insert(-w);
}
for(int i=1; i<=n; ++i){
maxn[i]=-*S[i].begin();
cnt+=updata(i);
}
printf("%lld\n", cnt>>1ll);
while(q--){
int k, e;
scanf("%d%d", &k, &e); k--;
int u=V[k].a, v=V[k].b, w=V[k].c;
V[k].c=e;
S[u].erase(-w); S[v].erase(-w);
S[u].insert(-e); S[v].insert(-e);
cnt-=updata(u)+updata(v);
sum[u]+=e-w; sum[v]+=e-w;
maxn[u]=-(*S[u].begin()); maxn[v]=-*S[v].begin();
cnt+=updata(u)+updata(v);
printf("%lld\n", cnt>>1ll);
}
}
D
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define A (a[0]+a[1]+a[2]+a[3])
#define A2 (a[0]+a[1]+a[2]+a[3])
#define B (b[0]+b[1]+b[2]+b[3])
int a[5], b[5];
int main(){
int T; cin>>T;
while(T--){
for(int i=0; i<4; ++i) scanf("%d", a+i);
for(int i=0; i<4; ++i) scanf("%d", b+i);
a[4]=b[4]=0;
while(A&&B){
if(a[4]){
a[4]--;
if(b[0]){
b[0]--; b[2]++;
continue;
}else if(b[1]){
b[1]--; b[3]++;
continue;
}else a[4]++;
}
if(a[2]){
a[2]--; a[4]++;
if(b[2]){
b[2]--; b[4]+=(A==0);
}else if(b[3]){
b[3]--;
}else if(b[0]){
b[0]--; b[2]++;
}else if(b[1]){
b[1]--; b[3]++;
}
}else if(a[0]){
a[0]--; a[2]++;
if(b[2]){
b[2]--; b[4]+=(A==0);
}else if(b[3]){
b[3]--;
}else if(b[0]){
b[0]--; b[2]++;
}else if(b[1]){
b[1]--; b[3]++;
}
}else if(a[1]){
a[1]--; a[3]++;
if(b[2]){
b[2]--; b[4]+=(A==0);
}else if(b[3]){
b[3]--;
}else if(b[0]){
b[0]--; b[2]++;
}else if(b[1]){
b[1]--; b[3]++;
}
}else if(a[3]){
a[3]--;
if(b[2]){
b[2]--; b[4]+=(A==0);
}else if(b[3]){
b[3]--;
}else if(b[0]){
b[0]--; b[2]++;
}else if(b[1]){
b[1]--; b[3]++;
}
}
}
puts(A||(B==0&&a[4]>b[4])?"Yes":"No");
}
return 0;
}
I
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int T; cin>>T;
while(T--){
int n; cin>>n; int mid=n>>1;
//对 1~mid内部的安排
for(int i=2; i<=mid; ++i) for(int j=1; j<i; ++j) printf("%d %d\n", j, i);
//对 mid+1 的特判
if(n&1) for(int i=1; i<=mid; ++i) printf("%d %d\n", i, mid+1);
//对 1~mid 与 (n-mid+1)~n 之间的安排
for(int i=n-mid+1; i<=n; ++i) for(int j=1; j<=n-i&&j<=mid; ++j) printf("%d %d\n", j, i);
for(int i=1; i<=mid; ++i) for(int j=n-i+1; j<=n; ++j) printf("%d %d\n", i, j);
//对 mid+1 的特判
if(n&1) for(int i=n-mid+1; i<=n; ++i) printf("%d %d\n", mid+1, i);
//对 (n-mid+1)~n 内部的安排
for(int i=n-mid+1; i<=n; ++i) for(int j=i+1; j<=n; ++j) printf("%d %d\n", i, j);
}
return 0;
}