SCOI2015 情报传递

Link

Diffculty

算法难度6,思维难度6,代码难度6

Description

由于原版题面有点儿复杂,我会简化许多。

给定一棵 n n n个点的树,每个点初始权值为 0 0 0,每个点有一个开关,如果开关开着,这个点的权值会每天加 1 1 1

m m m个操作,分为两种:

  1. 打开一个点 x x x的开关,保证这个操作对每个点至多进行一次。
  2. 查询 x x x y y y的路径上有多少个点权值大于 C C C

1 ≤ n , m ≤ 2 × 1 0 5 1\le n,m\le 2\times 10^5 1n,m2×105

Solution

我们考虑在什么情况下,修改 j j j会对询问 i i i造成贡献。

假设询问 i i i的限制为 C C C,那么满足 i − j > C i-j>C ij>C即可。

其中 i − j i-j ij为修改 j j j对应的点在第 i i i天的权值。

我们对式子变形可得 j &lt; i − C j&lt;i-C j<iC,这样我们可以转化问题了。

打开开关相当于将这个点权值设为 i i i,查询相当于查询权值小于 i − C i-C iC的点的个数。

这个问题我们可以整体二分+树状数组。

整体二分之后,我们可以忽略权值的影响了,直接变成单点加,链上求和。

这个问题我们差分之后,就变成了子树加,单点求值了,这个可以简单地用dfs序上建立树状数组来维护。

我们得到了一个 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的做法,实际上有更优的做法,我这里不多做介绍。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=' ';
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10+(ch^48),ch=getchar();
	return f==1?x:-x;
}
const int N=2e5+5;
int n,m,tot,root,cnt;
int head[N],to[N<<1],Next[N<<1];
inline void addedge(int x,int y){
	to[++tot]=y;
	Next[tot]=head[x];
	head[x]=tot;
}
int dfn[N],ed[N],dfn_clock;
int st[N][18],dep[N];
inline void dfs(int x,int fa){
	dfn[x]=++dfn_clock;
	st[x][0]=fa;
	dep[x]=dep[fa]+1;
	for(int i=1;i<=17;++i)st[x][i]=st[st[x][i-1]][i-1];
	for(int i=head[x];i;i=Next[i]){
		int u=to[i];
		if(u==fa)continue;
		dfs(u,x);
	}
	ed[x]=dfn_clock;
}
inline int getlca(int x,int y){
	if(x==y)return x;
	if(dep[x]<dep[y])swap(x,y);
	for(int i=17;i>=0;--i)
		if(dep[st[x][i]]>=dep[y])
			x=st[x][i];
	if(x==y)return x;
	for(int i=17;i>=0;--i)
		if(st[x][i]!=st[y][i])
			x=st[x][i],y=st[y][i];
	return st[x][0];
}
int t[N];
inline void modify(int x,int v){
	while(x<=n){
		t[x]+=v;
		x+=x&-x;
	}
}
inline int query(int x){
	int ans=0;
	while(x){
		ans+=t[x];
		x-=x&-x;
	}
	return ans;
}
int ans1[N],ans2[N];
struct data{
	int id,opt,x,y,v,lca;
	data(){}
	data(int _id,int _opt,int _x,int _y,int _v,int _lca)
		:id(_id),opt(_opt),x(_x),y(_y),v(_v),lca(_lca){}
};
vector<data> q;
inline void solve(vector<data> &t,int l,int r){
	if(l==r)return;
	int mid=(l+r)>>1;
	for(vector<data>::iterator it=t.begin();it!=t.end();++it){
		if((*it).opt==1 && (*it).v>mid){
			int num=-query(dfn[(*it).lca]);
		    num-=query(dfn[st[(*it).lca][0]]);
			num+=query(dfn[(*it).x]);
			num+=query(dfn[(*it).y]);
			ans2[(*it).id]+=num;
		}
		else if((*it).opt==2 && (*it).v<=mid){
			modify(dfn[(*it).x],1);
			modify(ed[(*it).x]+1,-1);
		}
	}
	for(vector<data>::iterator it=t.begin();it!=t.end();++it){
		if((*it).opt==2 && (*it).v<=mid){
		    modify(dfn[(*it).x],-1);
			modify(ed[(*it).x]+1,1);
		}
	}
	vector<data> ls,rs;
	for(vector<data>::iterator it=t.begin();it!=t.end();++it){
		if((*it).v<=mid){
			ls.push_back(*it);
		}
		else{
			rs.push_back(*it);
		}
	}
	solve(ls,l,mid);
	solve(rs,mid+1,r);
}
int main(){
	n=read();
	for(int i=1;i<=n;++i){
		int x=read();
		if(x)addedge(x,i);
		else root=i;
	}
	dfs(root,0);
	m=read();
	for(int i=1;i<=m;++i){
		int opt=read();
		if(opt==1){
			int x=read(),y=read(),v=read();
			int lca=getlca(x,y);
			if(v>=i)ans2[i]=0;
			else q.push_back(data(i,opt,x,y,i-v,lca));
			ans1[i]=dep[x]+dep[y]-dep[lca]-dep[st[lca][0]];
		}
		else{
			int x=read();
			q.push_back(data(i,opt,x,0,i,0));
		}
	}
	solve(q,1,m);
	for(int i=1;i<=m;++i){
		if(ans1[i])printf("%d %d\n",ans1[i],ans2[i]);
	}
	return 0;
}
中描述了一个幼儿园里分配糖果的问题,每个小朋友都有自己的要求。问题的输入包括两个整数NN和KK,表示幼儿园里的小朋友数量和要满足的要求数量。接下来的KK行表示小朋友们的要求,每行有三个数字,XX,AA,BB。如果X=1,表示第AA个小朋友分到的糖果必须和第BB个小朋友分到的糖果一样多;如果X=2,表示第AA个小朋友分到的糖果必须少于第BB个小朋友分到的糖果;如果X=3,表示第AA个小朋友分到的糖果必须不少于第BB个小朋友分到的糖果;如果X=4,表示第AA个小朋友分到的糖果必须多于第BB个小朋友分到的糖果;如果X=5,表示第AA个小朋友分到的糖果必须不多于第BB个小朋友分到的糖果。这个问题可以被看作是一个差分约束系统的问题。 具体地说,可以使用差分约束系统来解决这个问题。差分约束系统是一种通过给变量之间的关系添加约束来求解最优解的方法。对于这个问题,我们需要根据小朋友们的要求建立约束条件,并通过解决这个约束系统来得出最小的糖果数量。 在问题的输入中,X的取值范围为1到5,分别对应不同的关系约束。根据这些约束,我们可以构建一个差分约束图。图中的节点表示小朋友,边表示糖果数量的关系。根据不同的X值,我们可以添加相应的边和权重。然后,我们可以使用SPFA算法(Shortest Path Faster Algorithm)来求解这个差分约束系统,找到满足所有约束的最小糖果数量。 需要注意的是,在读取输入时需要判断X和Y是否合法,即是否满足X≠Y。如果X=Y,则直接输出-1,因为这种情况下无法满足约束条件。 综上所述,为了满足每个小朋友的要求,并且满足所有的约束条件,我们可以使用差分约束系统和SPFA算法来求解这个问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【差分约束系统】【SCOI2011】糖果 candy](https://blog.youkuaiyun.com/jiangzh7/article/details/8872699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [P3275 [SCOI2011]糖果(差分约束板子)](https://blog.youkuaiyun.com/qq_40619297/article/details/88678605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值