CF的VP记录
CodeTON Round 5 (Div. 1 + Div. 2, Rated, Prizes!)
vp时间:2023.7.11
总结记录:考场上过了前三题,第四题没看懂题面,感觉看懂了就会了,第五题不会dp爆寄,还是要总结提升dp能力
A.Tenzing and Tsondu
因为宝可梦之间对抗是小的死掉,大的变成差,所以我们发现,把 \(a_1,a_2,b_1,b_2\) 比出结果,等价于把 \(a_1+a_2,b_1+b_2\) 比较
因此,我们可以直接把所有宝可梦的能力值加起来比较,还是相当显然的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100;
ll a[maxn],b[maxn],sum=0,T,n,m;
int main(){
scanf("%lld",&T);
while(T-->0){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) sum+=a[i];
for(int i=1;i<=m;i++) scanf("%lld",&b[i]);
for(int i=1;i<=m;i++) sum-=b[i];
if(sum>0) puts("Tsondu");
else if(sum==0) puts("Draw");
else puts("Tenzing");
for(int i=1;i<=n;i++) a[i]=0;
for(int i=1;i<=m;i++) b[i]=0;
sum=0;
}
return 0;
}
B.Tenzing and Books
我们发现,因为按位或满足以下性质:
1、 \(a|b|a = a|b\)
2、 每一位之间独立
3、任何数与1按位或都是1,只有0和0按位或才能产生一个0
因此,我们考虑把取数的约束关系给破坏掉,可以发现如果我们取了 \(a_i\) ,就一定会取 \(a_1,a_2,\cdots ,a_{i-1}\) ,我们可以维护每个序列的按位或前缀和,然后先后的约束关系就没有了
接下来我们考虑如何凑数,不难发现,如果 \(x\) 的某一位上是 1 ,那么待检验数的对应位上是任何数都无所谓;但如果 \(x\) 的某一位上是 0 ,那么待检验数的对应位上是就只能是0。不然就不能取这个数。
因此,我们要保证选取的数都满足上述条件,如果都取了还是凑不到,那么就一定无解。
时间复杂度 \(O(n \log x)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5;
ll a[maxn],b[maxn],c[maxn],sum=0,T,n,m,bits[32],bita[maxn][32],bitb[maxn][32],bitc[maxn][32],visa[maxn],visb[maxn],visc[maxn];
int main(){
scanf("%lld",&T);
while(T-->0){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++) a[i]=(a[i]|a[i-1]),b[i]=(b[i]|b[i-1]),c[i]=(c[i]|c[i-1]);
for(int i=0;i<=31;i++) bits[i]=0;
for(int i=0;i<=31;i++) bits[i]=(m>>i&1);
for(int i=1;i<=n;i++){
for(int j=0;j<=31;j++){
if(a[i]>>j&1) bita[i][j]=1;
if(b[i]>>j&1) bitb[i][j]=1;
if(c[i]>>j&1) bitc[i][j]=1;
}
}
for(int i=1;i<=n;i++) visa[i]=visb[i]=visc[i]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=31;j++){
if(bita[i][j]==1&&bits[j]==0) visa[i]=0;
if(bitb[i][j]==1&&bits[j]==0) visb[i]=0;
if(bitc[i][j]==1&&bits[j]==0) visc[i]=0;
}
}
ll temp=0;
for(int i=1;i<=n;i++){
temp=temp|(visa[i]*a[i]);
temp=temp|(visb[i]*b[i]);
temp=temp|(visc[i]*c[i]);
}
if(temp==m||m==0) puts("Yes");
else puts("No");
for(int i=1;i<=n;i++) a[i]=0;
for(int i=1;i<=n;i++) b[i]=0;
for(int i=1;i<=n;i++) c[i]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=31;j++){
visa[i]=0,visb[i]=0,visc[i]=0;
bita[i][j]=bitb[i][j]=bitc[i][j]=0;
}
}
}
return 0;
}
C.Tenzing and Balls
不难发现,这道题目中对于操作次数没有限制,只询问了最优解,因此考虑dp
为了方便,调换题目中 \(i,j\) 的大小关系。
设状态 \(f_i\) 表示前 \(i\) 个数里最多能删 \(f_i\) 个数
我们可以得到状态转移方程:
\(f_i = \max(f_{j-1}+i-j+1,f_{i-1})\)
其中 \(a_i = a_j\)
我们发现,这个方程的复杂度瓶颈在于如何快速求出前半段,我们可以额外维护一个数组,用来表示每个不同的数的最大 \(f_{j-1}-j+1\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5,inf=1e9;
int a[maxn],f[maxn],pos[maxn];
int main(){
int T;
scanf("%d",&T);
while(T-->0){
int n,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) f[i]=pos[i]=-inf;
for(int i=1;i<=n;i++){
f[i]=max(pos[a[i]]+1+i,f[i-1]);
pos[a[i]]=max(f[i-1]-i,pos[a[i]]);
ans=max(ans,f[i]);
}
printf("%d\n",ans);
}
return 0;
}
D.Tenzing and His Animal Friends
赞歌
E.Tenzing and Triangle
赞歌
F.Tenzing and Tree
对于每一个 \(k\) 和给定的根节点 \(rt\) 我们都能发现,对于边 \((u,v)\) ,设 \(v\) 为 \(u\) 的父亲, \(siz[u]\) 的含义是 \(u\) 的子树中有 \(siz[u]\) 个黑点
因此,这条边的权值就是 \(\left| k-siz[u]*2 \right|\) ,整棵树的权值就是 \(\sum\limits_{u \ne rt} \left| k-siz[u]*2 \right|\)
我们发现,这个绝对值很烦,试图破掉它
为了拆开绝对值,我们考虑什么情况下 \(siz[u]*2\) 一定小于 \(k\) ?
当 \(rt\) 是以黑点关键点的虚树的重心时,一定有 \(siz[u]*2 \leq k\)
所以当 \(rt\) 被钦定为重心之后,整棵树的权值就是 $\sum\limits_{u \ne rt} k-siz[u]*2 $
我们可以枚举每一个点是重心时的答案,并且这样做正确性是有保证的。
首先,我们转化后的答案是一定不大于原答案的,并且虚树重心一定是原树中的某个点,因此转化是正确的
同时,当 \(u=rt\) 时, \(siz[u]=k\)
因此,我们再进行提取,原树权值为$ (n+1)k - \sum\limits_{u} siz[u]2 $
现在,我们要看一看如何快速求出来 \(\sum\limits_{u} siz[u]*2\) 的最小值
对于每一个黑点,包含它的 \(u\) 一定都在它到 \(rt\) 的路径上,因此,\(\sum\limits_{u} siz[u]*2 = \sum\limits_{u \in Black} dep[u]\)
因此,原式的最小值就是最小的 \(k\) 个 \(dep\)
总结一下,我们只需要枚举黑点虚树的重心,并且对于每一个重心,求出黑点个数在0到k之间的答案并且取最大值即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4;
int n,dep[maxn],ans[maxn];
vector<int>v[maxn];
void dfs(int x,int fx){
dep[x]=dep[fx]+1;
for(int i=0;i<v[x].size();i++){
int s=v[x][i];
if(s==fx) continue;
dfs(s,x);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
memset(ans,0x3f,sizeof(ans));
for(int rt=1;rt<=n;rt++){
for(int i=1;i<=n;i++) dep[i]=0;
int sum=0;
dfs(rt,rt);
sort(dep+1,dep+1+n);
for(int k=0;k<=n;k++) ans[k]=min(ans[k],sum+=dep[k]);
}
for(int i=0;i<=n;i++) printf("%d ",(n+1)*i-2*ans[i]);
return 0;
}