我做过的Ynoi题

P5354 [Ynoi Easy Round 2017] 由乃的 OJ

题意

给你一个有 \(n\) 个点的树,每个点的包括一个位运算 \(opt\) 和一个权值 \(x\),位运算有&|^ 三种,分别用 \(1,2,3\) 表示。

每次询问包含三个整数 \(x,y,z\),初始选定一个数 \(v\)。然后 \(v\) 依次经过从 \(x\)\(y\) 的所有节点,每经过一个点 \(i\)\(v\) 就变成 \(v\ opt_i\ x_i\),所以他想问你,最后到 \(y\) 时,希望得到的值尽可能大,求最大值。给定的初始值 \(v\) 必须是在 \([0,z]\) 之间。

每次修改包含三个整数 \(x,y,z\),意思是把 \(x\) 点的操作修改为 \(y\),数值改为 \(z\)

其中, \(1 \leq n,m \leq 10 ^ 5\)\(1 \leq k \leq 64\) ,其中 \(k\) 的意义是每个点上的数和询问中的 \(z\) 都小于 \(2 ^ k\)

解法

发现运算过程中不同的二进制位之间没有任何影响,可以分开考虑。

我们从高位向低位考虑,每次贪心的选取即可。

现在加上了修改操作,怎么做?

线段树+树剖

有一个显然的做法是树剖+线段树来维护树上修改和询问。

对于每一个位,我们维护从左向右和从右向左得到的是 \(0\) 还是 \(1\) ,询问的时候直接合并区间即可。

复杂度 \(O(nk \log ^ 2 n)\) ,这显然是无法通过的,考虑把 \(k\) 给优化掉。

优化

我们可以把一堆二进制位压进一个 long long 里面。

因为二进制位之间是没有任何影响的,所以可以合并。

那么有 \(f_0[u][i]=(f_0[l][i]\;bitand\;f_1[r][i])\;bitor\;(!f_0[l][i]\;bitand\;f_0[r][i])\)

其他同理。

复杂度 \(O(n \log ^ 2 n)\) ,可以通过。

代码

#include<bits/stdc++.h>
#define ll unsigned long long
#define ls rt*2
#define rs ls+1
#define mid (l+r)/2
#define lson ls,l,mid
#define rson rs,mid+1,r
using namespace std;
const int N=1e5+100;
int n,m,k;
int fa[N],dep[N],son[N],dfn[N],dfncnt,topf[N],sz[N];
struct node{
	ll lans[2],rans[2];//lans:从左到右 rans:从右到左
}t[N*8];
vector<int>g[N];
void dfs1(int u,int f,int d){
	fa[u]=f,dep[u]=d+1;
	sz[u]++;
	for(int v:g[u]){
		if(v==f)continue;
		dfs1(v,u,d+1);
		if(sz[v]>sz[son[u]])son[u]=v;
		sz[u]+=sz[v];
	}
}
void dfs2(int u,int f,int tf){
	topf[u]=tf,dfn[u]=++dfncnt;
	if(son[u])dfs2(son[u],u,tf);
	for(int v:g[u]){
		if(v==f||v==son[u])continue;
		dfs2(v,u,v);
	}
}
node merge(node l,node r){
	node res;
	res.lans[0]=(~l.lans[0]&r.lans[0])|(l.lans[0]&r.lans[1]);
	res.lans[1]=(~l.lans[1]&r.lans[0])|(l.lans[1]&r.lans[1]);
	res.rans[0]=(~r.rans[0]&l.rans[0])|(r.rans[0]&l.rans[1]);
	res.rans[1]=(~r.rans[1]&l.rans[0])|(r.rans[1]&l.rans[1]);
	return res;
}
void pushup(int rt,int l,int r){
	t[rt]=merge(t[ls],t[rs]);
}
void update(int rt,int l,int r,int x,int opt,ll v){
	if(l==r){
		if(opt==1)t[rt].lans[0]=t[rt].rans[0]=0,t[rt].lans[1]=t[rt].rans[1]=v;
		if(opt==2)t[rt].lans[0]=t[rt].rans[0]=v,t[rt].lans[1]=t[rt].rans[1]=-1;
		if(opt==3)t[rt].lans[0]=t[rt].rans[0]=v,t[rt].lans[1]=t[rt].rans[1]=(~v);
		return;
	}
	if(x<=mid)update(lson,x,opt,v);
	else update(rson,x,opt,v);
	pushup(rt,l,r);
}
node query(int rt,int l,int r,int L,int R){
	if(L<=l&&R>=r)return t[rt];
	if(L<=mid&&R>mid)return merge(query(lson,L,R),query(rson,L,R));
	if(L<=mid)return query(lson,L,R);
	return query(rson,L,R);
}
ll pathquery(int u,int v,ll z){
	int cnt=0;
	node ures={{0,(ll)-1},{0,(ll)-1}},vres={{0,(ll)-1},{0,(ll)-1}};
	while(topf[u]!=topf[v]){
		int tu=topf[u],tv=topf[v];
		if(dep[topf[u]]<dep[topf[v]]){
			swap(u,v);
			swap(tu,tv);
			swap(ures,vres);
			cnt++;
		}
		ures=merge(query(1,1,n,dfn[tu],dfn[u]),ures);
		u=fa[tu];
	}
	if(dep[u]<dep[v])swap(u,v),swap(ures,vres),cnt++;
	swap(vres.lans,vres.rans);
	ures=merge(vres,merge(query(1,1,n,dfn[v],dfn[u]),ures));
	if(!(cnt%2))swap(ures.lans,ures.rans);
	ll ans=0,now=0;
	for(int i=k-1;i>=0;i--){
		if(ures.lans[0]&(1ull<<i))ans+=(1ull<<i);
		else if((ures.lans[1]&(1ull<<i))&&now+(1ull<<i)<=z){
			ans+=(1ull<<i);
			now+=(1ull<<i);
		}
	}
	return ans;
}
ll val[N];int op[N];
int main(){
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=n;i++)scanf("%d %llu",&op[i],&val[i]);
	for(int i=1,u,v;i<n;i++){
		scanf("%d %d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs1(1,0,1);
	dfs2(1,0,1);
	for(int i=1;i<=n;i++)update(1,1,n,dfn[i],op[i],val[i]);
	while(m--){
		int op,x,y;ll z;
		scanf("%d %d %d %llu",&op,&x,&y,&z);
		if(op==1)printf("%llu\n",pathquery(x,y,z));
		else update(1,1,n,dfn[x],y,z);
	}
}

P5309 [Ynoi2011] 初始化

题意

给定长为 \(n\) 的序列 \(a_i\) ,有两种操作 \(m\)

  • x,y,z:将下标除以 \(x\)\(y\) 的数增加 \(z\)
  • l,r :求 \([l,r]\)区间中所有数的和,答案对 \(10 ^ 9 + 7\) 取模。

其中,\(1 \leq n,m \leq 2 \times 10 ^ 5\)

解法

如果操作 \(1\)\(x\) 很大,那么修改的数就会很少。

于是考虑根号分治,设 \(B\) 为块长。


\(x \gt B\)

直接暴力做,但是如果用树状数组那么修改复杂度为 \(O(\frac{n\log n}{B})\) ,时间复杂度有点高。

考虑分块,每 \(\sqrt{n}\) 个数为一个块,修改复杂度为 \(O(\frac{n}{B})\) ,查询复杂度为 \(O(\sqrt{n})\)


\(x \leq B\)

因为修改的数实在太多了,考虑打标记。

\(f_{x,y}\) 表示查询 \(x,y\)\(z\) 的总和。

修改复杂度为 \(O(1)\) ,查询复杂度为 \(O(B ^ 2)\)

这时 \(B\)\(n ^ {\frac{2}{3}}\) 最优,但是总复杂度为 \(O(n ^ {\frac{5}{3}})\) ,无法通过。

发现如果 \(x\) 相同, \(y\) 变化,可以理解为一些下标在左右平移,于是可以前缀和优化,查询复杂度为 \(O(B)\)

这时 \(B\)\(\sqrt{n}\) 最优,总复杂度为 \(O(n \sqrt{n})\)

在这个题中 \(B\) 不宜过大,开 \(100\)\(250\) 最优。

代码

代码很短。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+100,s=120,M=N/s+100,mod=1e9+7;
int n,m,a[N],b[M],id[N],bl[N],br[N],add[s+10][s+10];ll ans;
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)id[i]=(i-1)/s+1;
	for(int i=1;i<=n;i++){
		if(id[i]!=id[i-1])bl[id[i]]=i;
		if(id[i]!=id[i+1])br[id[i]]=i;
	}
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)(b[id[i]]+=a[i])%=mod;
	while(m--){
		int op,l,r,k;
		scanf("%d %d %d",&op,&l,&r);
		if(op==1){
			scanf("%d",&k);
			if(l>s)for(int i=r;i<=n;i+=l)(b[id[i]]+=k)%=mod,(a[i]+=k)%=mod;
			else for(int i=r;i<=l;i++)(add[l][i]+=k)%=mod;
		}else{
			ans=0;
			if(id[l]==id[r])for(int i=l;i<=r;i++)ans+=a[i];
			else{
				for(int i=l;i<=br[id[l]];i++)ans+=a[i];
				for(int i=id[l]+1;i<=id[r]-1;i++)ans+=b[i];
				for(int i=bl[id[r]];i<=r;i++)ans+=a[i];
			}
			for(int i=1;i<=s;i++)ans+=(ll)r/i*add[i][i]%mod+add[i][r%i]-(ll)(l-1)/i*add[i][i]%mod-add[i][(l-1)%i];
			ans=(ans%mod+mod)%mod;
			printf("%lld\n",ans);
		}
	}
}

P5356 [Ynoi Easy Round 2017] 由乃打扑克

题意

给定长度为 \(n\) 的序列 \(a_i\) ,需要支持 \(m\) 次操作:

  1. \([l,r]\) 区间中的数加 \(k\)
  2. 查询 \([l,r]\) 中第 \(k\) 小的数。

解法

前言

一开始看到这道题我还以为是树套树板子,结果发现是假的。

正片

考虑分块,设块长为 \(B\)

我们将原数组复制一份,在块内排序。

修改

对于整块,直接打标记即可,对于散块,直接暴力修改,暴力重新排序。

复杂度 \(O(\frac{n}{B} + B\log B)\)

查询

考虑二分答案,设当前二分的中点为 \(mid\) ,只需求有几个数比 \(mid\) 小。

对于整块,直接二分即可,对于散块,直接暴力求。

复杂度 \(O(B + \frac{n\log V}{B})\)

其中,\(B\)\(\sqrt{n}\log n\) 时复杂度最优为 \(O(q\sqrt{n}\log n)\) ,但在实际实现时,要取小一点。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+100,s=200;
const ll inf=1e12+100;
ll a[N],b[N],tag[N];
int n,q,B,id[N],bl[N],br[N];
void update1(int l,int r,int k){
	for(int i=l;i<=r;i++)a[i]+=k;
	for(int i=bl[id[l]];i<=br[id[r]];i++)b[i]=a[i];
	sort(b+bl[id[l]],b+br[id[l]]+1);
}
void update(int l,int r,int k){
	if(id[l]==id[r]){
		update1(l,r,k);
		return;
	}
	update1(l,br[id[l]],k);
	for(int i=id[l]+1;i<=id[r]-1;i++)tag[i]+=k;
	update1(bl[id[r]],r,k);
}
int ch(int l,int r,int x){
	int num=0;
	if(id[l]==id[r]){
		for(int i=l;i<=r;i++)if(a[i]+tag[id[l]]<=x)num++;
		return num;
	}
	for(int i=l;i<=br[id[l]];i++)if((a[i]+tag[id[l]])<=x)num++;
	for(int i=id[l]+1;i<=id[r]-1;i++){
		if(b[bl[i]]+tag[i]>x)continue;
		if(b[br[i]]+tag[i]<=x){
			num+=br[i]-bl[i]+1;
			continue;
		}
		int L=bl[i],R=br[i];
		while(L<R){
			int mid=(L+R)/2+1;
			if(b[mid]+tag[i]<=x)L=mid;
			else R=mid-1;
		}
		if(b[L]+tag[i]<=x)num+=L-bl[i]+1; 
	}
	for(int i=bl[id[r]];i<=r;i++)if((a[i]+tag[id[r]])<=x)num++;
	return num;
}
ll getmax(int l,int r){
	ll res=-inf;
	if(id[l]==id[r]){
		for(int i=l;i<=r;i++)res=max(res,a[i]+tag[id[l]]);
		return res;
	}
	for(int i=l;i<=br[id[l]];i++)res=max(res,a[i]+tag[id[l]]);
	for(int i=id[l]+1;i<=id[r]-1;i++)res=max(res,b[br[i]]+tag[i]);
	for(int i=bl[id[r]];i<=r;i++)res=max(res,a[i]+tag[id[r]]);
	return res;
}
ll getmin(int l,int r){
	ll res=inf;
	if(id[l]==id[r]){
		for(int i=l;i<=r;i++)res=min(res,a[i]+tag[id[l]]);
		return res;
	}
	for(int i=l;i<=br[id[l]];i++)res=min(res,a[i]+tag[id[l]]);
	for(int i=id[l]+1;i<=id[r]-1;i++)res=min(res,b[bl[i]]+tag[i]);
	for(int i=bl[id[r]];i<=r;i++)res=min(res,a[i]+tag[id[r]]);
	return res;
}
ll query(int L,int R,int k){
	if(k<1||k>R-L+1)return -1;
	ll l=getmin(L,R),r=getmax(L,R),ans=-1;
	while(l<=r){
		int mid=(l+r)/2;
		if(ch(L,R,mid)<k){
			l=mid+1;
		}else{
			r=mid-1;
			ans=mid;
		}
	}
	return ans;
}
int main(){
	scanf("%d %d",&n,&q);	
	for(int i=1;i<=n;i++){scanf("%lld",&a[i]);b[i]=a[i];}
	B=(n-1)/s+1;
	for(int i=1;i<=n;i++)id[i]=(i-1)/s+1;
	for(int i=1;i<=n;i++){
		if(id[i]!=id[i-1])bl[id[i]]=i;
		if(id[i]!=id[i+1])br[id[i]]=i;
	}
	for(int i=1;i<=B;i++)sort(b+bl[i],b+br[i]+1);
	while(q--){
		int op,l,r,k;
		scanf("%d %d %d %d",&op,&l,&r,&k);
		if(op==1){
			printf("%lld\n",query(l,r,k));
		}else{
			update(l,r,k);
		}
	}
}
posted @ 2026-09-18 21:19  _lipeiran  阅读(5)  评论(0)    收藏  举报