CF Round 1001 题解合集
草怎么是贪心专场。
问就是不会 F。
C
注意到操作一至多只会进行一次,进行两次就抵消了。
所以直接枚举所有可能操作取最大值即可。
注意要开 long long。
总体复杂度 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,ans,a[51],b[51],c[51];
void get(int cnt){
memcpy(c,b,sizeof(c));
for(int i=1;i<=n-cnt;i++){
b[i]=c[i+1]-c[i];
}
}
int solve(int now){
memcpy(b,a,sizeof(b));
int sum=0,ans=0;
for(int i=1;i<=n;i++){
ans+=b[i];
}
int cnt=1;
while(cnt<n){
if(now==cnt) reverse(b+1,b+1+n-cnt+1);
get(cnt);
sum=0;
for(int i=1;i<=n-cnt;i++){
sum+=b[i];
}
ans=max(ans,sum);
cnt++;
}
return ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
ans+=a[i];
}
for(int i=0;i<n;i++){
ans=max(ans,solve(i));
}
cout<<ans<<'\n';
ans=0;
}
return 0;
}
D
草感觉比 E 困难啊。
为了方便,我们强制每次操作的 相连。
首先钦定 为根,假设 是已知的,不难发现对于每次操作 ,它们是相互独立的,只影响 两点的相对点权。
想要最小化点权,那么对于每个点,应该这样操作:
-
如果 ,那么令 ,使得 ;
-
如果 ,那么令 ,使得 。
不难发现,这样操作后,点权为 。此处认为 的父亲是 。
所以,我们的问题转化为最小化 。
不难发现,在没有区间限制的前提下,令 是最优的。
加上区间限制之后,结论依然成立。
注意对于叶子节点来说,令 是最优的。
按题意模拟贪心策略,总体复杂度 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,maxn,l[200005],r[200005],x,y;
int head[200005],nxt[400005],target[400005],tot;
void add(int x,int y){
tot++;
nxt[tot]=head[x];
head[x]=tot;
target[tot]=y;
}
int f[200005],a[200005];
void dfs(int x,int fa){
f[x]=fa;
bool flag=true;
for(int i=head[x];i;i=nxt[i]){
int y=target[i];
if(y==fa) continue;
dfs(y,x);
a[x]=max(a[x],max(l[x],a[y]));
flag=false;
}
if(flag) a[x]=l[x];
a[x]=min(a[x],r[x]);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>l[i]>>r[i];
}
for(int i=1;i<n;i++){
cin>>x>>y;
add(x,y);
add(y,x);
}
dfs(1,0);
for(int i=1;i<=n;i++){
maxn+=max(0ll,a[i]-a[f[i]]);
}
cout<<maxn<<'\n';
for(int i=1;i<=n;i++){
head[i]=0;
a[i]=0;
f[i]=0;
}
tot=maxn=0;
}
}
E1
经过手玩样例发现,对于 ,如果存在 不在 子树内且 ,此时 最大的 一定是必胜点。
原因是,这样的 满足,无论后手操作任意 ,先手都无法再次操作。
考虑用反证法证明。
假设操作完 后,依然存在两点 ,使得后手操作 后,先手依然可以操作 ,不难发现 。
考虑分类讨论:
-
如果 存在子孙关系,那么只能是 是 的祖先,否则 不是 最大的点, 才是。而如果 是 的祖先,那么操作完 后无法再操作 ,矛盾;
-
如果 不存在子孙关系,那么 一定可以取代 成为点权最大的点,矛盾。
所以,我们的结论是正确的。
实现上,可以用树状数组记录不同点权点的出现次数,在 dfs 的过程中差分即可。
总体复杂度 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,x,y,ans,a[400005],b[400005],c[400005],dfn[400005];
int head[400005],nxt[800005],target[800005],tot;
void add(int x,int y){
tot++;
nxt[tot]=head[x];
head[x]=tot;
target[tot]=y;
}
bool cmp(int x,int y){
return a[x]>a[y];
}
#define lowbit(i) (i&(-i))
void modify(int x,int k){
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=k;
}
}
int query(int x){
int ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
void dfs(int x,int fa){
int now=query(n)-query(a[x]);
modify(a[x],1);
for(int i=head[x];i;i=nxt[i]){
int y=target[i];
if(y==fa) continue;
dfs(y,x);
}
if(b[x]==query(n)-query(a[x])-now) b[x]=0;
else b[x]=1;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
modify(a[i],1);
dfn[i]=i;
}
for(int i=1;i<=n;i++){
b[i]=query(n)-query(a[i]);
}
for(int i=1;i<n;i++){
cin>>x>>y;
add(x,y);
add(y,x);
}
dfs(1,0);
sort(dfn+1,dfn+1+n,cmp);
for(int i=1;i<=n;i++){
if(b[dfn[i]]){
ans=dfn[i];
break;
}
}
cout<<ans<<'\n';
ans=tot=0;
for(int i=1;i<=n;i++){
head[i]=b[i]=c[i]=0;
}
}
return 0;
}
E2
草感觉是一个题,会 E1 就会 E2。
根据 E1 的结论,当前局面是必败局面当且仅当不存在一个点 ,使得 子树外的点 满足 。
所以,对于先手来说,他只能一步取胜,也就是一步将局面变成必败局面。
然后,考虑哪些 满足在操作完它之后,当前局面是必败局面。
不难发现,这样的 ,对于任意 ,应当满足:
-
是 的祖先;
-
令 表示满足点权 且不在 子树内的点的共同 lca, 是 的祖先。
两条满足任意一个即可。也很好理解,要么 不存在,要么让后手选 之后必输。
然后按点权从大到小加入点,用 dfs 序线段树维护每个 对应的 ,再用一个树状数组做树上差分即可。
总体复杂度 ,常数很小,可以通过。
#include<bits/stdc++.h>
using namespace std;
int t,n,m,x,y,a[400005],num,tmp1,tmp2;
vector<int> ans,v[400005];
int head[400005],nxt[800005],target[800005],tot;
void add(int x,int y){
tot++;
nxt[tot]=head[x];
head[x]=tot;
target[tot]=y;
}
int siz[400005],dfn[400005],rnk[400005],hson[400005],top[400005],dep[400005],f[400005],cnt;
void dfs1(int x,int fa){
siz[x]=1;
for(int i=head[x];i;i=nxt[i]){
int y=target[i];
if(y==fa) continue;
dep[y]=dep[x]+1;
f[y]=x;
dfs1(y,x);
siz[x]+=siz[y];
if(siz[hson[x]]<siz[y]) hson[x]=y;
}
}
void dfs2(int x,int t){
cnt++;
dfn[x]=cnt;
rnk[cnt]=x;
top[x]=t;
if(!hson[x]) return;
dfs2(hson[x],t);
for(int i=head[x];i;i=nxt[i]){
int y=target[i];
if(y==f[x] || y==hson[x]) continue;
dfs2(y,y);
}
}
int lca(int x,int y){
if(!x) return y;
if(!y) return x;
while(top[x]^top[y]){
if(dep[top[x]]>dep[top[y]]) x=f[top[x]];
else y=f[top[y]];
}
if(dfn[x]<dfn[y]) return x;
else return y;
}
int val[800005],ls[800005],rs[800005],dcnt,rt;
void build(int l,int r,int &x){
x=++dcnt;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,ls[x]);
build(mid+1,r,rs[x]);
}
void modify(int l,int r,int pos,int x){
if(l==r){
val[x]=rnk[l];
return;
}
int mid=(l+r)>>1;
if(pos<=mid) modify(l,mid,pos,ls[x]);
else modify(mid+1,r,pos,rs[x]);
val[x]=lca(val[ls[x]],val[rs[x]]);
}
int query(int l,int r,int ql,int qr,int x){
if(ql<=l && r<=qr) return val[x];
int mid=(l+r)>>1,ans=0;
if(ql<=mid) ans=lca(ans,query(l,mid,ql,qr,ls[x]));
if(qr>=mid+1) ans=lca(ans,query(mid+1,r,ql,qr,rs[x]));
return ans;
}
int c[400005];
#define lowbit(i) (i&(-i))
void chg(int x,int k){
if(!x) return;
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=k;
}
}
int get(int x){
int ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
void init(){
for(int i=1;i<=n;i++){
v[i].clear();
head[i]=dfn[i]=rnk[i]=siz[i]=hson[i]=top[i]=c[i]=0;
}
for(int i=1;i<=dcnt;i++){
val[i]=ls[i]=rs[i]=0;
}
ans.clear();
cnt=tot=num=dcnt=rt=0;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
init();
for(int i=1;i<=n;i++){
cin>>a[i];
v[a[i]].push_back(i);
}
for(int i=1;i<n;i++){
cin>>x>>y;
add(x,y);
add(y,x);
}
dfs1(1,0);
dfs2(1,1);
build(1,n,rt);
for(int i=n;i>=1;i--){
for(int j=0;j<v[i].size();j++){
x=v[i][j];
if(dfn[x]==1) tmp1=0;
else tmp1=query(1,n,1,dfn[x],rt);
if(dfn[x]+siz[x]-1==n) tmp2=0;
else tmp2=query(1,n,dfn[x]+siz[x],n,rt);
y=lca(tmp1,tmp2);
if(!y) continue;
//cout<<"qq"<<x<<' '<<get(dfn[x]+siz[x]-1)<<' '<<get(dfn[x]-1)<<'\n';
if(get(dfn[x]+siz[x]-1)-get(dfn[x]-1)==num) ans.push_back(x);
}
for(int j=0;j<v[i].size();j++){
x=v[i][j];
if(dfn[x]==1) tmp1=0;
else tmp1=query(1,n,1,dfn[x],rt);
if(dfn[x]+siz[x]-1==n) tmp2=0;
else tmp2=query(1,n,dfn[x]+siz[x],n,rt);
y=lca(tmp1,tmp2);
//cout<<"qwq"<<x<<' '<<y<<'\n';
if(y){
num++;
if(x==y){
chg(dfn[x],1);
}else{
chg(dfn[x],1);
chg(dfn[y],1);
chg(dfn[lca(x,y)],-1);
}
}
}
for(int j=0;j<v[i].size();j++){
x=v[i][j];
modify(1,n,dfn[x],rt);
}
}
sort(ans.begin(),ans.end());
cout<<ans.size()<<' ';
for(int i=0;i<ans.size();i++){
cout<<ans[i]<<' ';
}
cout<<'\n';
}
return 0;
}
本文作者:Kenma
本文链接:https://www.cnblogs.com/Kenma/p/18697012
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步