刷题

CF780G Andryusha and Nervous Barriers

比较简单,先按照高度排序并重编号,扫描线找边缘下落的球分裂的第一块板子,这部分可以线段树二分,找编号最大的要求高度超过它的板子。

可以发现依赖关系一定是编号大依赖编号小,可以递推。

点击查看代码
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int N=1e5+5,mod=1e9+7;
int h,w,n,cnt,ch[N][2],p[N],sum[N];
struct node{
	int h,l,r,s;
}a[N];
vector<int> ve[N],v1[N],v2[N];
bool cmp(node x,node y)
{
	return x.h<y.h;
}
struct seg_tr{
	int l,r,mx;
}tr[N*4];
void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
}
void update(int id,int x,int v)
{
	if(tr[id].l==tr[id].r)
	{
		tr[id].mx=v;
		return;
	}
	int mid=(tr[id].l+tr[id].r)>>1;
	if(x<=mid) update(lid,x,v);
	else update(rid,x,v);
	tr[id].mx=max(tr[lid].mx,tr[rid].mx);
}
int find(int id,int x,int v)
{
	if(x==0||tr[id].mx<v) return n+1;
	if(tr[id].l==tr[id].r) return tr[id].l;
	int mid=(tr[id].l+tr[id].r)>>1,t=n+1;
	if(x>mid) t=find(rid,x,v);
	if(t==n+1) return find(lid,x,v);
	return t;
}
void insert(int x)
{
	if(!ch[x][0]) ch[x][0]=ch[x][1]=find(1,x-1,a[x].h);
	else ch[x][1]=find(1,x-1,a[x].h);
}
int main()
{
	scanf("%d%d%d",&h,&w,&n);
	if(n==0)
	{
		printf("%d",w);
		return 0;
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&a[i].h,&a[i].l,&a[i].r,&a[i].s);
		a[i].s+=a[i].h;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		v1[a[i].l].push_back(i),v2[a[i].r].push_back(i);
		if(a[i].l!=1) ve[a[i].l-1].push_back(i);
		if(a[i].r!=w) ve[a[i].r+1].push_back(i);
	}
	build(1,1,n);
	for(int i=1;i<=w;i++)
	{
		for(int j=0;j<v1[i].size();j++)
		{
			update(1,v1[i][j],a[v1[i][j]].s);
		}
		for(int j=0;j<ve[i].size();j++)
		{
			insert(ve[i][j]);
		}
		p[i]=find(1,n,h+1);
		for(int j=0;j<v2[i].size();j++)
		{
			update(1,v2[i][j],0);
		}
	}
	sum[n+1]=1;
	for(int i=1;i<=n;i++)
	{
		sum[i]=(sum[ch[i][0]]+sum[ch[i][1]])%mod;
//		printf("%d %d\n",ch[i][0],ch[i][1]);
	}
	int ans=0;
	for(int i=1;i<=w;i++) ans=(ans+sum[p[i]])%mod;
	printf("%d",ans);
	return 0;
}

CF1578B Building Forest Trails

首先把圆拍到序列上,要求每条边 \(x<y\),如果两条边 \((x_1,y_1),(x_2,y_2)\) 满足 \(x_1\le x_2\le y_1 \le y_2\) ,那么就会交叉。

考虑对每个连通块的连边方式做一个改变,变为按顺序从小到大连,而且观察到每个连通块构成的区间要么包含,要么不交。

这里应该会有一个 \(\log^2\) 的做法,就是记录一下 pre 和 next,然后线段树二分。

实际上还能更优,考虑记录每个 \(x\) 上方的线的数量 \(h_x\),不包括连到 \(x\) 的线。

可以发现相邻两个点之间的 \(h_x\) 的变化量不超过 1,因为要么在上一个点开了一条新线,要么在这里收回一条线。

接下来考虑哪些线会和 \((x,y)\) 交叉,一种开头在 \(x\) 前面,结尾在中间,那么必然有一条线经过 \(x\),因为区间相互包含或不交,那么在中间的收束点的 \(h\) 一定是比 \(h_x\) 小的。

所以每次可以选 \(h_x,h_y\) 中较大的去找,然后合并区间。

合并分为两种情况,一是不交,那么前面右端连后面左端,一种是包含,那么就是去掉覆盖小区间的那一段,再加上小区间到大区间的两条边,其实就是小区间上的覆盖条数 -1。

点击查看代码
#include<cstdio>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int N=2e5+5;
int n,m,f[N],l[N],r[N],ans[N*2],cnt;
struct seg_tr{
	int l,r,mn,lazy;
}tr[N*4];
int find(int x)
{
	if(x!=f[x]) f[x]=find(f[x]);
	return f[x];
}
void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
}
void pushdown(int id)
{
	if(tr[id].lazy)
	{
		tr[lid].lazy+=tr[id].lazy,tr[lid].mn+=tr[id].lazy;
		tr[rid].lazy+=tr[id].lazy,tr[rid].mn+=tr[id].lazy;
		tr[id].lazy=0;
	}
}
void update(int id,int l,int r,int v)
{
	if(l>r) return;
	if(tr[id].l==l&&tr[id].r==r)
	{
		tr[id].mn+=v,tr[id].lazy+=v;
		return;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) update(lid,l,r,v);
	else if(l>mid) update(rid,l,r,v);
	else update(lid,l,mid,v),update(rid,mid+1,r,v);
	tr[id].mn=min(tr[lid].mn,tr[rid].mn);
}
int query(int id,int x)
{
	if(tr[id].l==tr[id].r)
	{
		return tr[id].mn;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(x<=mid) return query(lid,x);
	else return query(rid,x);
}
int findl(int id,int x,int v)
{
	if(tr[id].mn>=v) return n+1;
	if(tr[id].l==tr[id].r) return tr[id].l;
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1,t=n+1;
	if(x<=mid) t=findl(lid,x,v);
	if(t==n+1) return findl(rid,x,v);
	return t;
}
int findr(int id,int x,int v)
{
	if(tr[id].mn>=v) return 0;
	if(tr[id].l==tr[id].r) return tr[id].l;
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1,t=0;
	if(x>mid) t=findr(rid,x,v);
	if(t==0) return findr(lid,x,v);
	return t;
}
void merge(int x,int y)
{
	if(l[x]>l[y]) swap(x,y);
	if(r[x]<l[y])
	{
		update(1,r[x]+1,l[y]-1,1);
//		printf("b1 %d %d\n",r[x]+1,l[y]-1);
	}
	else
	{
		update(1,l[y],r[y],-1);
//		printf("a-1 %d %d %d %d\n",l[x],r[x],l[y],r[y]);
	}
	r[x]=max(r[x],r[y]),f[y]=x;
}
int main()
{
	scanf("%d%d",&n,&m);
	build(1,1,n);
	for(int i=1;i<=n;i++)
	{
		f[i]=l[i]=r[i]=i;
	}
	while(m--)
	{
		int opt,x,y;
		scanf("%d%d%d",&opt,&x,&y);
		if(x>y) swap(x,y);
		if(opt==2)
		{
			if(find(x)==find(y)) ans[++cnt]=1;
			else ans[++cnt]=0;
			continue;
		}
		if(find(x)==find(y)) continue;
		int v1=query(1,x),v2=query(1,y);
//		printf("%d %d\n",v1,v2);
		while(find(x)!=find(y))
		{
			if(v1>=v2)
			{
				int t=findl(1,x+1,v1);
				if(t>=y) break;
				merge(find(x),find(t));
				v1=query(1,x);
			}
			else
			{
				int t=findr(1,y-1,v2);
//				printf("%d\n",t);
				if(t<=x) break;
				merge(find(y),find(t));
				v2=query(1,y);
			}
		}
		if(find(x)!=find(y)) merge(find(x),find(y));
	}
	for(int i=1;i<=cnt;i++)
	{
		printf("%d",ans[i]);
	}
	return 0;
}

CF1083C Max Mex

先考虑怎么处理一组询问,就是依次加入每个点,然后判断是否在一条链上。

然后考虑上树,这里可以维护一个更强的限制,就是每个区间 \([l,r]\) 内的是否都在一条链上。

合并上就是判断两个链是否能拼在一起,一种方法就是维护端点,然后一侧往另一侧插入,最后线段树二分。

点击查看代码
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int N=2e5+5;
int n,head[N],edgenum,cnt,t[N*2],b[N];
int dep[N],dfn[N],siz[N],idx,p[N],in[N];
struct edge{
	int to,nxt;
}e[N];
void add_edge(int u,int v)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
void dfs(int u,int fa)
{
	dep[u]=dep[fa]+1,dfn[u]=++idx;
	siz[u]=1,in[u]=cnt+1;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		t[++cnt]=u;
		dfs(v,u);
		siz[u]+=siz[v];
	}
	t[++cnt]=u;
}
struct ST_table{
	int l2[N*2],st[N*2][20],mn[N*2][20];
	void build()
	{
		for(int i=1;i<=cnt;i++)
		{
			st[i][0]=t[i],mn[i][0]=dep[t[i]];
			if(i!=1) l2[i]=l2[i/2]+1;
		}
		for(int i=1;i<20;i++)
		{
			for(int j=1;j+(1<<i)-1<=cnt;j++)
			{
				mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);
				if(mn[j][i-1]==mn[j][i]) st[j][i]=st[j][i-1];
				else st[j][i]=st[j+(1<<(i-1))][i-1];
			}
		}
	}
	int query(int l,int r)
	{
		l=in[l],r=in[r];
		if(l>r) swap(l,r);
		int k=l2[r-l+1];
		if(mn[l][k]<mn[r-(1<<k)+1][k]) return st[l][k];
		return st[r-(1<<k)+1][k];
	}
}ST;
struct seg_tr{
	int l,r,x,y,fl;
}tr[N*4];
bool check(int x,int y)
{
	if(dfn[x]<=dfn[y]&&dfn[x]+siz[x]>dfn[y]) return 1;
	return 0;
}
pair<int,int> merge(int x,int y,int z)
{
	if(dep[x]>dep[y]) swap(x,y);
	if(check(y,z)) return {x,z};
	if(check(x,y))
	{
		if(check(z,y)&&check(x,z)) return {x,y};
		int t=ST.query(z,y);
		if(check(t,x)) return {y,z};
		return {0,0};
	}
	if(check(x,z)) return {z,y};
	int t=ST.query(x,y);
	if(check(t,z)&&(check(z,x)||check(z,y))) return {x,y};
	return {0,0};
}
void pushup(int id)
{
	tr[id].fl=tr[lid].fl&tr[rid].fl;
	if(!tr[id].fl) return;
	pair<int,int> p=merge(tr[lid].x,tr[lid].y,tr[rid].x);
	if(p.first==0)
	{
		tr[id].fl=0;
		return;
	}
	p=merge(p.first,p.second,tr[rid].y);
	if(p.first==0) tr[id].fl=0;
	else tr[id].x=p.first,tr[id].y=p.second;
}
void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r;
	if(l==r)
	{
		tr[id].fl=1,tr[id].x=tr[id].y=b[l];
		return;
	}
	int mid=(tr[id].l+tr[id].r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
//	printf("%d %d %d\n",tr[id].l,tr[id].r,tr[id].fl);
}
void update(int id,int x,int v)
{
	if(tr[id].l==tr[id].r)
	{
		tr[id].x=tr[id].y=v;
		return ;
	}
	int mid=(tr[id].l+tr[id].r)>>1;
	if(x<=mid) update(lid,x,v);
	else update(rid,x,v);
	pushup(id);
}
int find(int id,int x,int y)
{
//	printf("%d %d\n",x,y);
	if(tr[id].l==tr[id].r) return tr[id].l;
	if(!tr[lid].fl) return find(lid,x,y);
	pair<int,int> p=merge(x,y,tr[lid].x);
	if(!p.first) return find(lid,x,y);
	p=merge(p.first,p.second,tr[lid].y);
	if(!p.first) return find(lid,x,y);
	return find(rid,p.first,p.second);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&p[i]);
		p[i]++;
		b[p[i]]=i;
	}
	for(int i=2;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		add_edge(x,i);
	}
	dfs(1,0);
	ST.build();
	build(1,1,n);
//	for(int i=1;i<=n;i++) printf("%d ",siz[i]);
	int q;
	scanf("%d",&q);
	while(q--)
	{
		int opt,x,y;
		scanf("%d",&opt);
		if(opt==2)
		{
			if(tr[1].fl)
			{
				printf("%d\n",n);
				continue;
			}
			printf("%d\n",find(1,b[1],b[1])-1);
		}
		else
		{
			scanf("%d%d",&x,&y);
			swap(p[x],p[y]),swap(b[p[x]],b[p[y]]);
			update(1,p[x],x),update(1,p[y],y);
		}
	}
	return 0;
}

P11833 [省选联考 2025] 推箱子

弱智但比较难写。

按时间排序,每次优先满足时间限制较紧的。考虑满足的过程中,以向左移动为例。

在移动的过程中考虑会碰到哪些箱子,记当前下标为 \(i\),那么现在移动的结果就是 \(b_i-k,b_i-k+1,\dots,b_i\),所以在它前面的点 \(j\),如果位置在 \(b_i-i+j\) 的后面,就要移动。

然后考虑如何快速找,这里给一个较为方便的解法,首先把箱子向右移动 \(i-j\),这样每个点的目标位置就是 \(b_i\),就可以线段树二分。

最后就是区间加等差数列和区间赋值等差数列,外加区间求和计算贡献,向右移动同理。

点击查看代码
#include<cstdio>
#include<algorithm>
#define ll long long
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int N=2e5+5;
int c,T,n,a[N],b[N];
ll t[N];
struct seg_tr{
	int l,r,cov,add,addt,mn,mx;
	ll sum;
}tr[N*4];
inline void clear(int id)
{
	tr[id].cov=-1,tr[id].add=tr[id].addt=0;
}
inline void pushup(int id)
{
	tr[id].sum=tr[lid].sum+tr[rid].sum;
	tr[id].mn=tr[lid].mn,tr[id].mx=tr[rid].mx;
}
inline void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r,clear(id);
	if(l==r)
	{
		tr[id].sum=tr[id].mn=tr[id].mx=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
inline void add(int id,int x,int y)
{
	int l=tr[id].l,r=tr[id].r;
	tr[id].mn+=x,tr[id].mx+=x+y*(r-l);
	tr[id].sum+=1ll*(2*x+y*(r-l))*(r-l+1)/2;
//	printf("%d %d %d %d %lld\n",l,r,tr[id].mn,tr[id].mx,tr[id].sum);
	tr[id].add+=x,tr[id].addt+=y;
}
inline void cover(int id,int v)
{
	tr[id].add=tr[id].addt=0;
	tr[id].mn=tr[id].mx=tr[id].cov=v;
	tr[id].sum=1ll*v*(tr[id].r-tr[id].l+1);
}
inline void pushdown(int id)
{
	if(tr[id].cov!=-1)
	{
		cover(lid,tr[id].cov),cover(rid,tr[id].cov);
		tr[id].cov=-1;
	}
	if(!tr[id].add&&!tr[id].addt) return;
	add(lid,tr[id].add,tr[id].addt);
	add(rid,tr[id].add+(tr[rid].l-tr[lid].l)*tr[id].addt,tr[id].addt);
	tr[id].add=tr[id].addt=0;
}
inline void update(int id,int l,int r,int x,int v)
{
	if(tr[id].l==l&&tr[id].r==r)
	{
//		printf("b");
//		if(r==6) printf("%d %d %d %d\n",l,r,x,v);
		add(id,x,v);
		return;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) update(lid,l,r,x,v);
	else if(l>mid) update(rid,l,r,x,v);
	else update(lid,l,mid,x,v),update(rid,mid+1,r,x+v*(mid+1-l),v);
	pushup(id);
}
inline void change(int id,int l,int r,int x,int v)
{
	if(tr[id].l==l&&tr[id].r==r)
	{
		cover(id,x);
		add(id,0,v);
		return;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) change(lid,l,r,x,v);
	else if(l>mid) change(rid,l,r,x,v);
	else change(lid,l,mid,x,v),change(rid,mid+1,r,x+v*(mid+1-l),v);
	pushup(id);
}
inline ll query(int id,int l,int r)
{
	if(tr[id].l==l&&tr[id].r==r)
	{
//		printf("%d %d %d\n",l,r,tr[id].sum);
		return tr[id].sum;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) return query(lid,l,r);
	else if(l>mid) return query(rid,l,r);
	else return query(lid,l,mid)+query(rid,mid+1,r);
}
inline int findl(int id,int x)
{
	if(tr[id].l==tr[id].r) return tr[id].l;
	pushdown(id);
	if(tr[lid].mx<=x) return findl(rid,x);
	return findl(lid,x);
}
inline int findr(int id,int x)
{
	if(tr[id].l==tr[id].r) return tr[id].l;
	pushdown(id);
	if(tr[rid].mn>=x) return findr(lid,x);
	return findr(rid,x);
}
struct node{
	ll t;
	int id;
}p[N];
inline bool cmp(node x,node y)
{
	return x.t<y.t;
}
inline ll move_left(int id,int st,int ed)
{
	update(1,1,id,id-1,-1);
	int x=findl(1,ed);
//	if(id==79) printf("a%d ",x);
	ll val=query(1,x,id)-1ll*(id-x+1)*ed;
	update(1,1,id,-id+1,1);
	change(1,x,id,ed-id+x,1);
	return val;
}
inline ll move_right(int id,int st,int ed)
{
	update(1,id,n,0,-1);
	int x=findr(1,ed);
	ll val=1ll*(x-id+1)*ed-query(1,id,x);
	update(1,id,n,0,1);
	change(1,id,x,ed,1);
	return val;
}
inline bool solve()
{
	ll tmp=0;
	for(int i=1;i<=n;i++)
	{
		int x=p[i].id;
		int now=query(1,x,x);
		if(now==b[x]) continue;
		if(now<b[x]) tmp+=move_right(x,now,b[x]);
		else tmp+=move_left(x,now,b[x]);
//		printf("%d %d %d %d %d\n",i,tmp,x,now,b[x]);
		if(tmp>t[x]) return 0;
	}
	return 1;
}
int main()
{
//	freopen("1.txt","r",stdin);
	scanf("%d%d",&c,&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%lld",&a[i],&b[i],&t[i]);
			p[i]=(node){t[i],i};
		}
//		ll sum=0;
//		for(int i=41;i<=139;i++) sum+=a[i];
//		printf("%lld\n",sum-(139-41)*(139-40)/2);
//		if(T>1||T==0) continue;
		sort(p+1,p+n+1,cmp);
		build(1,1,n);
//		for(int i=1;i<=n;i++) printf("%d ",query(1,i,i));
		if(solve()) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

P11831 [省选联考 2025] 追忆

因为是自己做的,所以比较神秘。

首先 bitset 搞出连通性,然后考虑对操作分块,但是还有 \(l,r\) 的限制,所以继续对值域分块。

这个时候可以对每个值域块跑 \(O(n)\) 的递推找最大值,散块暴力判断。被操作的部分因为值不确定,所以单独拿出来求出值后求贡献。

这里注意一下常数,对每个操作块所有整块一起跑,然后在询问处取需要的,这样常数比较小。

块长因为 \(n,q\) 同阶,所以可以取到 \(n^{\frac{2}{3}}\),时间复杂度 \(O(\frac{n^2}{w}+n^{\frac{5}{3}})\)

点击查看代码
#include<bits/stdc++.h>
#define il inline
using namespace std;
const int N=1e5+5;
const int Inf = 2e9;
bool Beg;
struct IO {
	static const int Size = (1 << 21);
	char buf[Size], *p1, *p2; int st[105], Top;
	~IO() {clear();}
	il void clear() {fwrite(buf, 1, Top, stdout); Top = 0;}
	il char gc() {return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, Size, stdin), p1 == p2) ? EOF : *p1++;}
	il void pc(const char c) {Top == Size && (clear(), 0); buf[Top++] = c;}
	il IO &operator >> (char &c) {while(c = gc(), c == ' ' || c == '\n' || c == '\r'); return *this;}
	template <typename T> il IO &operator >> (T &x) {
		x = 0; bool f = 0; char ch = gc();
		while(!isdigit(ch)) {if(ch == '-') f = 1; ch = gc();}
		while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc();
		f ? x = -x : 0; return *this;
	}
    il IO &operator >> (string &s) {
        s = ""; char c = gc();
        while(c == ' ' || c == '\n' || c == '\r') c = gc();
        while(c != ' ' && c != '\n' && c != '\r' && c != EOF) s += c, c = gc();
        return *this;
    }
	il IO &operator << (const char c) {pc(c); return *this;}
	template <typename T> il IO &operator << (T x) {
		if(x < 0) pc('-'), x = -x;
		do st[++st[0]] = x % 10, x /= 10; while(x);
		while(st[0]) pc('0' + st[st[0]--]);
		return *this;
	}
	il IO &operator << (const char *s) {for(int i = 0; s[i]; i++) pc(s[i]); return *this;}
}fin, fout;	
int ct,T,n,m,q,head[N],edgenum,cnt1,cnt2,b1,b2,id[N];
int d[N],a[N],b[N],p[N],st[N],ed[N],pos[N],cnt;
int d1[N],mx[36][N],ans[N],maxn;
bool vis[N];
struct edge{
	int nxt,to;
}e[N*2];
struct node{
	int opt,x,y;
}c[N];
struct ques{
	int t,x,l,r;
}qs[N];
bitset<N> bs[N],I;
inline void add_edge(int u,int v)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
inline void init()
{
	for(int i=1;i<=n;i++) bs[i]=I;
	memset(ans,0,sizeof(ans));
	memset(head,0,sizeof(head));
	memset(d,0,sizeof(d));
	edgenum=cnt1=cnt2=0;
}
inline void change(int l,int r)
{
	for(int i=l;i<=r;i++)
	{
		int x=c[i].x,y=c[i].y;
		if(c[i].opt==2) swap(b[x],b[y]);
		else swap(a[x],a[y]),p[a[x]]=x,p[a[y]]=y;
	}
}
int t1,t2;
inline void get()
{
	memset(mx,0,sizeof(mx));
	for(int i=1;i<=n;i++)
	{
		if(!vis[p[i]]) mx[pos[i]][p[i]]=b[p[i]];
	}
	for(int u=n;u>0;u--)
	{
		for(int i=head[u];i;i=e[i].nxt)
		{
			for(int j=1;j<=t1;j++) mx[j][u]=max(mx[j][u],mx[j][e[i].to]);
		}
	}
}
inline void solve(int x)
{
//	if(ans[x]==n) return;
	int l=qs[x].l,r=qs[x].r;
	for(int i=1;i<=cnt;i++)
	{
		int t=id[i];
		if(a[t]>=l&&a[t]<=r&&bs[qs[x].x][t]) ans[x]=max(ans[x],b[t]); 
	}
	for(int i=l;i<=min(r,ed[pos[l]]);i++)
	{
		if(bs[qs[x].x][p[i]]) ans[x]=max(ans[x],b[p[i]]);
	}
	if(pos[l]==pos[r]) return;
	for(int i=pos[l]+1;i<pos[r];i++) ans[x]=max(ans[x],mx[i][qs[x].x]);
	for(int i=st[pos[r]];i<=r;i++)
	{
		if(bs[qs[x].x][p[i]]) ans[x]=max(ans[x],b[p[i]]);
	}
}
int main()
{
//	freopen("1.txt","r",stdin);
//	freopen("2.txt","w",stdout);
	fin>>ct>>T;
//	T=1;
//	printf("%d %d\n",ct,T);
	while(T--)
	{
		fin>>n>>m>>q;
		init();
		for(int i=1;i<=m;i++)
		{
			int u,v;
			fin>>u>>v;
			add_edge(u,v);
			d[u]++;
		}
		for(int i=1;i<=n;i++) bs[i][i]=1;
		for(int u=n;u>0;u--)
		{
			for(int i=head[u];i;i=e[i].nxt)
			{
				bs[u]|=bs[e[i].to];
			}
		}
		for(int i=1;i<=n;i++)
		{
			fin>>a[i];
			p[a[i]]=i;
		}
		for(int i=1;i<=n;i++)
		{
			fin>>b[i];
		}
		for(int i=1;i<=q;i++)
		{
			int opt,x,y,z;
			fin>>opt>>x>>y;
			if(opt==1||opt==2) c[++cnt1]=(node){opt,x,y};
			else
			{
				fin>>z;
				qs[++cnt2]=(ques){cnt1,x,y,z};
			}
		}
		b1=3100,b2=pow(cnt1,2.0/3.0)*1.7;
		t1=(n+b1-1)/b1;
//		printf("%d ",t1);
		for(int i=1;i<=t1;i++)
		{
			st[i]=(i-1)*b1+1,ed[i]=i*b1;
		}
		ed[t1]=n;
		for(int i=1;i<=t1;i++)
		{
			for(int j=st[i];j<=ed[i];j++) pos[j]=i;
		}
		if(b2==0)
		{
			get();
			for(int j=1;j<=cnt2;j++)
			{
				solve(j);
				fout<<ans[j]<<'\n';
			}
			continue;
		}
		t2=(cnt1+b2-1)/b2;
//		printf("%d ",t1);
//		printf("%d %d\n",n,cnt1);
//		printf("%d %d %d %d %d %d\n",n,cnt1,b1,b2,t1,t2);
		int tmp=1;
		for(int i=1;i<=t2;i++)
		{
//			printf("%d ",i);
			int st1=(i-1)*b2+1,ed1=min(cnt1,i*b2);
			cnt=0;
			int k=tmp;
			while(k<=cnt2&&qs[k].t<=ed1) k++;
			if(k==tmp) continue;
			for(int j=st1;j<=ed1;j++)
			{
				if(!vis[c[j].x]) vis[c[j].x]=1,id[++cnt]=c[j].x;
				if(!vis[c[j].y]) vis[c[j].y]=1,id[++cnt]=c[j].y;
			}
			get();
//			printf("%d %d\n",k,tmp);
			for(int j=tmp;j<k;j++)
			{
				change(max(st1,qs[j-1].t+1),qs[j].t);
				solve(j);
			}
			change(qs[k-1].t+1,ed1);
			for(int j=st1;j<=ed1;j++) vis[c[j].x]=vis[c[j].y]=0;
			tmp=k;
		}
		for(int i=1;i<=cnt2;i++)
		{
			fout<<ans[i]<<'\n';
		}
	}
	return 0;
}
posted @ 2026-02-28 10:17  wangsiqi2010916  阅读(20)  评论(0)    收藏  举报