[43] (CSP 集训) CSP-S 模拟 10

B.清扫

考虑从叶子节点往上推

首先可以发现的几个性质

  • 子树内需要消除的数,要么通过子树根节点 “发送” 到上面(只经过子树内一个叶节点),要么通过自己的叶节点解决
  • 对于子树内既不是根也不是叶节点的节点,节点上的值只能由这一支路的叶节点消除,所以如果他节点上的值和下面节点 “发送” 上来的值不相等时,无解

然后考虑怎么去计算子树根节点向上的 “发送” 数量

设这个数量为 \(k\),子树各支路 “发送” 到子树根节点的数量总和为 \(s\),子树根节点的权值为 \(a\),可以发现

  • 子树内解决会为 \(s\) 带来 \(-2\) 的贡献
  • “发送” 出去会为 \(s\) 带来 \(-1\) 的贡献
  • 无论何种操作,每次操作总会给 \(a\) 带来 \(-1\) 的贡献

因此:

\[k+2(a-k)=s \]

由此可以唯一确定 \(k\),将这个值继续往上传即可

例图

这是一个 \(k=0\) 的图

需要注意的

  • 由题有 \(0\le k\le a\),否则无解
  • 我们在上述式子中只判断了数量关系,但实际上,由于子树内必须要选择不同的叶子配对,因此可能会出现有叶子无法使用的情况,此时是无解的

这种情况的判断方式:看看子节点里是不是有比这个点自己还大的,有就报告无解

  • 注意 \(n=2\) 的情况需要特判
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001];
vector<int>e[100001];
int degree[100001];
bool flag=false;
int dfs(int now,int last){
	int res=0,cnt=0,maxn=0;
	for(int i:e[now]){
		if(i!=last){
			cnt++;
			int r=dfs(i,now);
			res+=r;maxn=max(maxn,r);
			if(flag) return 0;
		}
	}
	if(cnt==0){   //叶子
		return a[now];
	}
	if(cnt==1){   //既不是根也不是叶节点,直接往上传
		if(res!=a[now]){
			flag=true;
			return 0;
		}
		return res;
	}
	if(res<a[now] or res>2*a[now]){   //判断解是否合法
		flag=true;
		return 0;
	}
	if(maxn>a[now]){   //判断是不是消完了
		flag=true;
		return 0;
	} 
	return 2*a[now]-res;  //继续传答案
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	if(n==2){
		cout<<(a[1]==a[2]?"YES":"NO")<<'\n';
		return 0;
	}
	for(int i=1;i<=n-1;++i){
		int x,y;scanf("%d %d",&x,&y);
		e[x].push_back(y);
		e[y].push_back(x);
		degree[x]++;degree[y]++;
	}
	for(int i=1;i<=n;++i){
		if(degree[i]!=1){  //不能从叶子开始搜
			if(dfs(i,0)){  //判断到整颗树根节点是不是全消完了
				flag=true;
			}
			break;
		}
	}
	cout<<(flag?"NO":"YES");
}

C.购物

怎么这么签,我要急了

发现可以直接对原数组排序

假设上一次操作造成了一个连通块(这个连通块的右端点一定是 \(sum\)

本次更新的时候判断一下:

  • 本次更新的区间与上一个区间有交

  • 本次区间与上一个区间中间有断点

这两种情况都是可以直接计算出新增贡献的

然后因为排序了,所以你的值是越来越大的,不用担心区间跑到左边的某个位置(包括之前没填上的缝隙),所以只看这个右端点就能做完

又写了一个啥用没有的头文件,还是用一下

感觉现在头文件一直在写底层的东西

但是不写总感觉不太舒服

#include<bits/stdc++.h>
#include<io.h>
#include<array.h>
using namespace std;
using namespace hdk;
#define int long long
array<int>a;
int sum=0,ans=0;
signed main(){
	a.read_array(fastio::read(),sort(a));
	for(array_iter i:a){
		int tmp=(i+1)/2;
		if(tmp>sum)  ans+=sum+i-tmp+1;
		else  ans+=i;
		sum+=i;
	}
	cout<<ans;
}

D.ants

回滚莫队板子题

这道题莫队是显然的,但是该怎么莫队

可以对值域维护信息,维护当其为左/右端点时,排列能扩展到的最大值

然后发现对于值域上的 \(i\),有 \(l_i=l_{i-1}+1\)(放到 \(i-1\) 后面),\(r\) 是同理的,这样你就能直接对着原序列扩展值域信息

不好删除,回滚,注意到 \(n,m\) 同阶,回滚别用 memcpy,开个栈弹一下就好了

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[200001];
int len,locate[200001];
struct ques{
	int l,r;
	int id;
	bool operator <(const ques &A)const{
		if(locate[l]==locate[A.l]) return r<A.r;
		return locate[l]<locate[A.l];
	}
}q[200001];
void prework(){
	len=sqrt(n);
	for(int i=1;i<=n;++i){
		locate[i]=i/len+(i%len!=0);
	}
}
int res=0,scroll_ans=0;
#define rbor(x) min(n,(locate[(x)])*len)
int llen[200001],rlen[200001];
stack<pair<int,int>> scroll_l,scroll_r;
void add(int x,int flag){
	llen[a[x]]=llen[a[x]-1]+1;
	rlen[a[x]]=rlen[a[x]+1]+1;
	int ret=llen[a[x]]+rlen[a[x]]-1;
	res=max(res,ret);
	if(flag){
		scroll_l.push({a[x]+rlen[a[x]]-1,llen[a[x]+rlen[a[x]]-1]});
		scroll_r.push({a[x]-llen[a[x]]+1,rlen[a[x]-llen[a[x]]+1]});
	}
	llen[a[x]+rlen[a[x]]-1]=rlen[a[x]-llen[a[x]]+1]=ret;
}
void scroll_back(int l,int r){
	res=scroll_ans;
	while(!scroll_l.empty()){
		llen[scroll_l.top().first]=scroll_l.top().second;
		scroll_l.pop();
	}
	while(!scroll_r.empty()){
		rlen[scroll_r.top().first]=scroll_r.top().second;
		scroll_r.pop();
	}
	for(int i=l;i<=r;++i){
		llen[a[i]]=rlen[a[i]]=0;
	}
}
void scroll_record(){
	scroll_ans=res;
	while(!scroll_l.empty()) scroll_l.pop();
	while(!scroll_r.empty()) scroll_r.pop();
}
int ans[200001];
int main(){
	freopen("ants.in","r",stdin);
	freopen("ants.out","w",stdout);
//	freopen("d.in","r",stdin);
//	freopen("test.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=m;++i){
		scanf("%d %d",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	prework();
	sort(q+1,q+m+1);
	int l=0,r=0;
	for(int i=1;i<=m;++i){
		if(locate[q[i].l]!=locate[q[i-1].l]){
			res=0;
			for(int j=1;j<=n;++j){
				llen[a[j]]=rlen[a[j]]=0;
			}
			r=rbor(q[i].l);
		}
		while(r<q[i].r) add(++r,false);
		scroll_record();
		l=min(q[i].r,rbor(q[i].l));
		for(int j=q[i].l;j<=l;++j) add(j,true);
		ans[q[i].id]=res;
		scroll_back(q[i].l,l);
	}
	for(int i=1;i<=m;++i){
		cout<<ans[i]<<"\n";
	}
} 
/*
8 3
3 1 7 2 5 8 6 4
1 4
5 8
1 7

*/

123456789

posted @ 2024-10-08 12:19  HaneDaniko  阅读(51)  评论(4编辑  收藏  举报