首页 > 其他分享 >数据结构做题记录(1)

数据结构做题记录(1)

时间:2024-11-04 20:31:39浏览次数:1  
标签:ch 记录 int 线段 lsh ans 数据结构 define

1: P11071 「QMSOI R1」 Distorted Fate

二进制的题,优先想到拆位求贡献。

因为是或,对于某一位,找到区间中最左的这一位为 \(1\) 的位置,然后相应的乘上它到右端点的距离,就可以计算答案了。

\(logV\) 是 \(30\),可以想到对二进制的每一位开一棵线段树,维护区间中这一位相关信息。

然后就是经典的线段树二分,很可惜这题空间有限。

不过我们发现,只关心区间中有没有 \(1\),所以直接用bool代替整形变量即可。

维护区间按位与和,按位或和,只在这两个值相等时更新,然后查询时依然线段树二分,不过实现细节与之前有所不同。

时空复杂度均为 \(O(n\space logn\space logV)\)。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define ROF(i,j,k) for(int i=j;i>=k;i--)
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*f;
}
const int N=2e5+10,M=30,mod=(1<<M);
int n,a[N],q;
struct SMT{
	struct node{bool x,y,tag;}tr[N<<2];
	void pushdown(int u){
		if(!tr[u].tag) return;
		if(tr[u<<1].x==tr[u<<1].y) tr[u<<1].x^=1,tr[u<<1].y^=1;
		if(tr[u<<1|1].x==tr[u<<1|1].y) tr[u<<1|1].x^=1,tr[u<<1|1].y^=1;
		tr[u<<1].tag^=1,tr[u<<1|1].tag^=1,tr[u].tag=0;
	}
	void pushup(int u){
		tr[u].x=tr[u<<1].x|tr[u<<1|1].x;
		tr[u].y=tr[u<<1].y&tr[u<<1|1].y;
	}
	void build(int u,int l,int r){
		if(l==r){tr[u].x=tr[u].y=a[l]&1;return;}
		int mid=(l+r)>>1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r),pushup(u);
	}
	void modify(int u,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr){
			if(tr[u].x==tr[u].y) tr[u].x^=1,tr[u].y^=1;
			tr[u].tag^=1;return; 
		}
		pushdown(u);int mid=(l+r)>>1;
		if(ql<=mid) modify(u<<1,l,mid,ql,qr);
		if(qr>mid) modify(u<<1|1,mid+1,r,ql,qr);
		pushup(u);
	}
	int find(int u,int l,int r){
		if(l==r) return l;
		pushdown(u);int mid=(l+r)>>1;
		if(tr[u<<1].x) return find(u<<1,l,mid);
		return find(u<<1|1,mid+1,r);
	}
	int query(int u,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr){
			if(tr[u].x) return find(u,l,r);
			return -1;
		}
		if(r<ql||l>qr) return -1;
		pushdown(u);int mid=(l+r)>>1,ans=-1;
		if(ql<=mid) ans=query(u<<1,l,mid,ql,qr);
		if(ans!=-1) return ans;
		if(qr>mid) ans=query(u<<1|1,mid+1,r,ql,qr);
		return ans;
	}
}S[M];
int main(){
	n=rd,q=rd;FOR(i,1,n) a[i]=rd;
	FOR(i,0,M-1){
		S[i].build(1,1,n+1);
		FOR(j,1,n) a[j]>>=1;
	}
	while(q--){
		int op=rd,l=rd,r=rd;
		if(op==1){
			int x=rd;
			FOR(i,0,M-1){
				if(x&1) S[i].modify(1,1,n+1,l,r);
				x>>=1;
			}
		}
		else{
			int ans=0;
			FOR(i,0,M-1){
				int pos=S[i].query(1,1,n+1,l,r);
				if(pos!=-1) ans=(ans+(1ll<<i)*(r-min(r+1,pos)+1)%mod)%mod;
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}

2:P4314 CPU 监控

区间加、区间赋值、区间最大值、区间历史最大值。

本题难点主要在 pushdown 操作和懒标记的下传。

当然用矩阵就不用思考这么多了:

  • 用广义矩阵乘法 \(C_{i,j}=\max_{k=1}^{m}(A_{i,k}+B_{k,j})\)。

  • 对区间加和区间赋值分别构造两个转移矩阵。

  • 线段树维护的矩阵为 \(\begin{bmatrix}mx & smx & 0\end{bmatrix}\),分别为区间最大值,区间历史最大值,以及 \(0\) 方便转移。

然后就做完了,难点来到卡常——不过这题太水,连卡常都不需要。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define ROF(i,j,k) for(int i=j;i>=k;i--)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
const int N=1e5+10;
const ll INF=1e12;
int n,q,a[N];
struct matrix{
	ll a[3][3];
	matrix(){memset(a,0,sizeof(a));}
	matrix operator+(matrix const &T){
		matrix res;
		FOR(i,0,2) FOR(j,0,2) res.a[i][j]=max(a[i][j],T.a[i][j]);
		return res;
	}
	matrix operator*(matrix const &T){
		matrix res;
		FOR(i,0,2) FOR(j,0,2) res.a[i][j]=-INF;
		FOR(i,0,2) FOR(j,0,2){
			res.a[i][j]=max(res.a[i][j],a[i][0]+T.a[0][j]);
			res.a[i][j]=max(res.a[i][j],a[i][1]+T.a[1][j]);
			res.a[i][j]=max(res.a[i][j],a[i][2]+T.a[2][j]);
		}
		return res;
	}
	bool operator==(matrix const &T){
		FOR(i,0,2) FOR(j,0,2) if(a[i][j]!=T.a[i][j]) return false;
		return true;
	}
}mx[N<<2],tag[N<<2],I,A;
matrix get_I(){
	matrix res;
	FOR(i,0,2) FOR(j,0,2) res.a[i][j]=-INF;
	FOR(i,0,2) res.a[i][i]=0;
	return res;
}
inline void pushdown(int u){
	if(tag[u]==I) return;
	tag[u<<1]=tag[u<<1]*tag[u],tag[u<<1|1]=tag[u<<1|1]*tag[u];
	mx[u<<1]=mx[u<<1]*tag[u],mx[u<<1|1]=mx[u<<1|1]*tag[u];
	tag[u]=I;
}
inline void build(int u,int l,int r){
	tag[u]=I;
	if(l==r){
		mx[u].a[0][0]=mx[u].a[0][1]=a[l],mx[u].a[0][2]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	mx[u]=mx[u<<1]+mx[u<<1|1];
}
inline void modify(int u,int l,int r,int ql,int qr,matrix v){
	if(ql<=l&&r<=qr){
		mx[u]=mx[u]*v,tag[u]=tag[u]*v;
		return;
	}
	pushdown(u);int mid=(l+r)>>1;
	if(ql<=mid) modify(u<<1,l,mid,ql,qr,v);
	if(qr>mid) modify(u<<1|1,mid+1,r,ql,qr,v);
	mx[u]=mx[u<<1]+mx[u<<1|1];
}
matrix query(int u,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr) return mx[u];
	pushdown(u);int mid=(l+r)>>1;matrix res=A;
	if(ql<=mid) res=res+query(u<<1,l,mid,ql,qr);
	if(qr>mid) res=res+query(u<<1|1,mid+1,r,ql,qr);
	return res;
}
int main(){	
	n=rd,I=get_I();FOR(i,1,n) a[i]=rd;
	build(1,1,n);
	FOR(i,0,2) FOR(j,0,2) A.a[i][j]=-INF;
	q=rd;
	while(q--){
		char op=getchar();int l=rd,r=rd;
		matrix res=query(1,1,n,l,r);
		if(op=='Q') printf("%lld\n",res.a[0][0]);
		else if(op=='A') printf("%lld\n",res.a[0][1]);
		else if(op=='P'){
			int x=rd;matrix v;
			v.a[0][0]=x,v.a[1][0]=v.a[2][0]=-INF,v.a[0][1]=x,v.a[0][2]=-INF,v.a[1][1]=v.a[2][2]=0,v.a[1][2]=v.a[2][1]=-INF;
			modify(1,1,n,l,r,v);
		}
		else{
			int x=rd;matrix v;
			v.a[0][0]=v.a[0][1]=v.a[0][2]=v.a[1][0]=v.a[1][2]=-INF,v.a[1][1]=0,v.a[2][0]=v.a[2][1]=x,v.a[2][2]=0;
			modify(1,1,n,l,r,v);
		}
	}
	return 0;
}

3:GSS2 - Can you answer these queries II

不算重复数字,按 HH 的项链的思路,很容易想到询问离线,右端点从小到大枚举。

主要还是最大子段和的计算,不能用之前的方法。

我们考虑当前右端点枚举到 \(i\),然后记 \(la_i=j\) 表示最近的 \(a_j=a_i\) 的 \(j\),然后区间 \([la_i+1,i]\) 加 \(a_i\)。

这时我们发现一个位置 \(j\) 上的数,就表示去重后的 \(\sum_{k=j}^{i}a_k\),对于询问 \([l,r],r=i\),查询 \([l,r]\) 中最大值即可。

然而这样还是错的,因为最大子段和不一定以 \(r\) 为右端点,所以我们再维护历史最大值即可。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
#define PII pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define ROF(i,j,k) for(int i=j;i>=k;i--)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
const int N=2e5+10,INF=1e5;
int n,a[N],la[N],q;ll ans[N],smx[N<<2],hs[N<<2],tag1[N<<2],tag2[N<<2];
vector<PII> ve[N];
void pushdown(int u){
	hs[u<<1]=max(hs[u<<1],smx[u<<1]+tag2[u]),hs[u<<1|1]=max(hs[u<<1|1],smx[u<<1|1]+tag2[u]);
	tag2[u<<1]=max(tag2[u<<1],tag1[u<<1]+tag2[u]),tag2[u<<1|1]=max(tag2[u<<1|1],tag1[u<<1|1]+tag2[u]);
	smx[u<<1]+=tag1[u],smx[u<<1|1]+=tag1[u];
	tag1[u<<1]+=tag1[u],tag1[u<<1|1]+=tag1[u];
	tag1[u]=tag2[u]=0;
}
void pushup(int u){
	smx[u]=max(smx[u<<1],smx[u<<1|1]);
	hs[u]=max(hs[u<<1],hs[u<<1|1]);
}
void modify(int u,int l,int r,int ql,int qr,int v){
	if(ql<=l&&r<=qr){
		smx[u]+=v,hs[u]=max(hs[u],smx[u]);
		tag1[u]+=v,tag2[u]=max(tag2[u],tag1[u]);
		return;
	}
	pushdown(u);int mid=(l+r)>>1;
	if(ql<=mid) modify(u<<1,l,mid,ql,qr,v);
	if(qr>mid) modify(u<<1|1,mid+1,r,ql,qr,v);
	pushup(u);
}
ll query(int u,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr) return hs[u];
	pushdown(u);int mid=(l+r)>>1;ll ans=0;
	if(ql<=mid) ans=max(ans,query(u<<1,l,mid,ql,qr));
	if(qr>mid) ans=max(ans,query(u<<1|1,mid+1,r,ql,qr));
	return ans;
}
int main(){
	n=rd;FOR(i,1,n) a[i]=rd;
	q=rd;FOR(i,1,q){int l=rd,r=rd;ve[r].push_back(mp(l,i));}
	FOR(i,1,n){
		modify(1,1,n,la[a[i]+INF]+1,i,a[i]),la[a[i]+INF]=i;
		for(auto j:ve[i]) ans[j.se]=query(1,1,n,j.fi,i);
	}
	FOR(i,1,q) printf("%lld\n",max(0ll,ans[i]));
	return 0;
}

4:P3081 [USACO13MAR] Hill Walk G

发现对于某条线段 \((x_0,y_0),(x_1,y_1)\),只有满足 \(x_0'\le x_1,x_1'>x_1\),且在 \(x=x_1\) 处的 \(y\) 值 \(\le y_1\) 的线段,才可以被降落到。

然后我们需要在这些线段中查询 \(x=x_1\) 时纵坐标最大的线段编号,这是李超线段树的经典操作。

因为随着走动, \(x\) 是不断增大的,所以按 \(x_0\) 排序,将满足条件的线段逐个加入,最后更新所在线段即可。

动态开点会 MLE,需要先将横坐标离散化。

本题细节有些多,需要注意只在李超线段树上修改查询时用离散化后的横坐标,其他时候任何计算,都用原来的坐标值。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
#define PDI pair<double,int>
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define ROF(i,j,k) for(int i=j;i>=k;i--)
#define debug cout<<"ILOVECCF!"<<endl;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*f;
}
const int N=3e5+10;
const double eps=1e-10,INF=2e18;
int n,seg[N<<2],lsh[N],cnt,tot,ld[N];
struct node{int x0,y0,x1,y1,l,r,t;}s[N];
bool cmp_s(node a,node b){
	if(a.x0!=b.x0) return a.x0<b.x0;
	return a.y1>b.y1;
}
struct segment{double k,b;}p[N];
double cal(int id,int x){return p[id].k*x+p[id].b;}
int cmp(double x,double y){
	if(x-y>eps) return 1;
	if(y-x>eps) return -1;
	return 0;
}
void update(int u,int l,int r,int id){
	int &g=seg[u],mid=(l+r)>>1;
	if(cmp(cal(id,lsh[mid]),cal(g,lsh[mid]))==1) swap(g,id);
	if(cmp(cal(id,lsh[l]),cal(g,lsh[l]))==1) update(u<<1,l,mid,id);
	if(cmp(cal(id,lsh[r]),cal(g,lsh[r]))==1) update(u<<1|1,mid+1,r,id);
}
void modify(int u,int l,int r,int ql,int qr,int id){
	if(ql<=l&&r<=qr){update(u,l,r,id);return;}
	int mid=(l+r)>>1;
	if(ql<=mid) modify(u<<1,l,mid,ql,qr,id);
	if(qr>mid) modify(u<<1|1,mid+1,r,ql,qr,id);
}
PDI pmx(PDI a,PDI b){
	if(cmp(a.fi,b.fi)==1) return a;
	return b;
}
PDI query(int u,int l,int r,int v){
	if(l>v||r<v) return mp(-INF,0);
	int mid=(l+r)>>1;PDI res=mp(cal(seg[u],lsh[v]),seg[u]);
	if(l==r) return res;
	return pmx(res,pmx(query(u<<1,l,mid,v),query(u<<1|1,mid+1,r,v)));
}
void make_seg(int x0,int y0,int x1,int y1){
	double k=1.0*(y1-y0)/(x1-x0),b=-k*x0+1.0*y0;
	p[++cnt]={k,b};
}
int main(){
	n=rd;
	FOR(i,1,n) s[i].x0=rd,s[i].y0=rd,s[i].x1=rd,s[i].y1=rd,lsh[++cnt]=s[i].x0,lsh[++cnt]=s[i].x1,lsh[++cnt]=s[i].x1-1,s[i].l=s[i].x0,s[i].r=s[i].x1,s[i].t=s[i].x1-1;
	sort(lsh+1,lsh+1+cnt),tot=unique(lsh+1,lsh+1+cnt)-lsh-1;
	sort(s+1,s+1+n,cmp_s),cnt=0;int idx=1,ans=1;
	FOR(i,1,n) s[i].l=lower_bound(lsh+1,lsh+1+tot,s[i].l)-lsh,s[i].r=lower_bound(lsh+1,lsh+1+tot,s[i].r)-lsh,s[i].t=lower_bound(lsh+1,lsh+1+tot,s[i].t)-lsh;
	FOR(i,1,n) make_seg(s[i].x0,s[i].y0,s[i].x1,s[i].y1);
	p[0].b=-INF;
	for(int i=2;;){
		for(;i<=n;i++){
			if(s[i].x0>s[idx].x1) break;
			if(s[i].x1<=s[idx].x1||cal(i,s[idx].x1)>s[idx].y1) continue;
			modify(1,1,tot,s[i].l,s[i].t,i);
		}
		int pos=query(1,1,tot,s[idx].r).se;
		if(!pos) break;
		ans++,idx=pos;
	}
	printf("%d\n",ans);
	return 0;
}

5:P11203 [JOIG 2024] 感染シミュレーション

容易发现,感染者存在的时间一定是一段连续的区间,考虑找到这些区间 \([s,t]\),然后问题就变为有多少右端点在 \([s,t]\) 中,且长度 \(\ge x\) 的线段,这是经典的二维偏序问题。

首先解决问题一,如何找到这些区间?不难发现,对于某条线段 \([l_i,r_i]\),我们只看 \(r_j>r_i\) 的 \(l_j\) 最小的线段,因为这样它们的交集才最大,如果这都不满足 \(\ge x\) 的条件的话,之后的传染就直接断了。

所以对于每条线段,先找到符合上述条件的线段作为它的后继,接下来可以倍增找到最后的满足条件的线段,只需判断交集最小值即可。

问题一解决,再考虑问题二,二维偏序问题,把所有询问离线下来,然后注意离散化即可。

特别地,离散化数组注意开到 \(4n\),因为要存的有询问左端点、右端点、区间长度、\(x-1\)(\(-1\) 是为了差分)这些值。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define ROF(i,j,k) for(int i=j;i>=k;i--)
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
const int N=1e5+10;
int n,f[N][20],fmn[N][20],q,lsh[N<<2],cnt,tot,id[N],tr[N<<2],ans[N];
struct node{int l,r;}seg[N];
bool cmp(int a,int b){return seg[a].r<seg[b].r;}
struct quest{int l,r,x;}que[N<<2];
struct Node{int id,x,op;};
vector<int> c[N<<2];vector<Node> v[N<<2];
int lowbit(int x){return x&-x;}
void add(int x,int v){for(int i=x;i<=tot;i+=lowbit(i))tr[i]+=v;}
int query(int x){int res=0;for(int i=x;i;i-=lowbit(i))res+=tr[i];return res;}
int main(){
	n=rd;
	FOR(i,1,n) seg[i].l=rd,seg[i].r=rd,id[i]=i,lsh[++cnt]=seg[i].r,lsh[++cnt]=seg[i].r-seg[i].l;
	sort(id+1,id+1+n,cmp);int pos=n,to=id[pos];
	ROF(i,n,1){
		while(pos>0&&seg[id[pos]].r>seg[id[i]].r){
			if(seg[id[pos]].l<seg[to].l) to=id[pos];
			pos--;
		}
		f[id[i]][0]=to,fmn[id[i]][0]=seg[id[i]].r-seg[to].l;
		FOR(j,1,18) f[id[i]][j]=f[f[id[i]][j-1]][j-1],fmn[id[i]][j]=min(fmn[id[i]][j-1],fmn[f[id[i]][j-1]][j-1]);
	}
	q=rd;
	FOR(i,1,q){
		int P=rd,x=rd,t=P;
		if(seg[P].r-seg[P].l<x){ans[i]=1;continue;}
		ROF(j,18,0) if(fmn[t][j]>=x) t=f[t][j];
		que[i]={seg[P].l+x-1,seg[t].r,x},lsh[++cnt]=seg[P].l+x-1,lsh[++cnt]=x-1;
	}
	sort(lsh+1,lsh+1+cnt),tot=unique(lsh+1,lsh+1+cnt)-lsh-1;cnt=0;
	FOR(i,1,q){
		if(ans[i]) continue;
		que[i].l=lower_bound(lsh+1,lsh+1+tot,que[i].l)-lsh;
		que[i].r=lower_bound(lsh+1,lsh+1+tot,que[i].r)-lsh;
		que[i].x=lower_bound(lsh+1,lsh+1+tot,que[i].x-1)-lsh;	
		int l=que[i].l,r=que[i].r,x=que[i].x;
		v[l].push_back({i,x,-1}),v[r].push_back({i,x,1});
	}
	FOR(i,1,n){
		int x=lower_bound(lsh+1,lsh+1+tot,seg[i].r)-lsh;
		int y=lower_bound(lsh+1,lsh+1+tot,seg[i].r-seg[i].l)-lsh;
		c[x].push_back(y);
	}
	int sum=0;
	FOR(i,1,tot){
		for(auto j:c[i]) add(j,1),sum++;
		for(auto j:v[i]){
			ans[j.id]+=j.op*(sum-query(j.x));
		}
	}
	FOR(i,1,q) printf("%d\n",ans[i]);
	return 0;
}

6:P11191 「KDOI-10」超级演出

二维偏序+根号分治。

首先发现对于第 \(i\) 个命令,会存在一个 \(w_i\) 使得执行完 \([w_i,i)\) 的命令后使得第 \(i\) 个命令的剧团能够出场。

如果 \(a_i\) 能直接到达 \(1\),显然 \(w_i=i\),否则 \(a_i=\max_{a_i\to v}\{w_v\}\)。

然后对于询问 \([l,r]\),需要满足 \(i\in [l,r],w_i\ge l\) 才可以出场,最终这段区间答案为 \(n-1-query(l,r)\),因为求的是候场的,这是经典二维偏序,离线询问枚举右端点可以 \(O(nlogn)\) 完成。

发现时间复杂度瓶颈在于求 \(w\) 数组,因为某个点出度可能非常大。但是我们发现出度大的点不会很多,这时候就可以考虑根号分治了。

设阈值 \(B\),对于出度 \(\le B\) 暴力计算,对于 \(\ge B\) 的点我们不去暴力算它,而是通过其他点对其更新。

时间复杂度 \(O(\frac{n^2}{B}+nB)\),利用基本不等式不难算出 \(B=\sqrt{n}\) 时最优。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
#define ll long long
#define PII pair<int,int>
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define ROF(i,j,k) for(int i=j;i>=k;i--)
#define debug cout<<"FUCKCCF!!!"<<endl;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*f;
}
const int N=4e5+10,M=450;
int c,n,m,k,q,cnt,deg[N],big[N],a[N],w[N],nxt[N],ans[N],tr[N],vis[N],la[N];
vector<int> v[N],e[N];vector<PII> que[N];
int lowbit(int x){return x&-x;}
void add(int x,int v){x++;for(int i=x;i<=k;i+=lowbit(i))tr[i]+=v;}
int query(int x){int res=0;x++;for(int i=x;i;i-=lowbit(i))res+=tr[i];return res;}
int main(){
	c=rd,n=rd,m=rd,k=rd,q=rd;int t=sqrt(m);
	FOR(i,1,m){int x=rd,y=rd;deg[x]++,v[x].push_back(y),vis[x]|=(y==1);}
	FOR(i,1,n) if(deg[i]>=t) big[i]=1;
	FOR(i,1,n){
		if(!big[i]) continue;
		for(auto j:v[i]) e[j].push_back(i);
	}
	FOR(i,1,k){
		a[i]=rd;
		if(vis[a[i]]) w[i]=i;
		else if(deg[a[i]]<t){for(auto j:v[a[i]]) w[i]=max(w[i],la[j]);}
		else w[i]=nxt[a[i]];
		la[a[i]]=w[i];
		for(auto j:e[a[i]]) nxt[j]=max(nxt[j],w[i]);
	}
	FOR(i,1,q){
		int l=rd,r=rd;
		que[r].push_back(make_pair(l,i));
	}
	memset(la,0,sizeof(la));
	FOR(i,1,k){
		add(w[la[a[i]]],1),add(w[i],-1),la[a[i]]=i;
		for(auto j:que[i]){
			ans[j.second]=n-1-query(j.first-1);
		}
	}
	FOR(i,1,q) printf("%d\n",ans[i]);
	return 0;
}

标签:ch,记录,int,线段,lsh,ans,数据结构,define
From: https://www.cnblogs.com/summ1t/p/18524329

相关文章

  • 学习思维导图和AI的记录
    mermaid代码为:graphLR  A-->A1[《HeadFirst嗨翻C语言》第九章]  A1-->B[函数指针]  A1-->C[动态内存分配]  A1-->D[结构体]  A1-->E[联合体]    B-->B1[声明]  B-->B2[使用]  B-->B3[回调函数]    C-......
  • C语言版数据结构算法(考研初试版—3)--链表定义、创建
    2、链表1、链表结构体typedefstructLNode{   intdata;   structLNode*next; }LNode,*LinkList; 2、遍历链表voidPrintList(LinkListL){   LinkListp=L->next;//Skiptheheadnodewhichisadummynode   while(p!=......
  • C语言版数据结构算法(考研初试版—4)--链表删除操作
    删除链表中值为m的结点(1)创建一个链表(2)打印删除前的链表(3)查找值为m的前一个结点(4)执行删除操作(5)打印删除后的链表#include<stdio.h>#include<stdlib.h>typedefstructLNode{ intdata; structLNode*next;}LNode,*LinkList;//头插法LinkListCreateList_L(){......
  • 数据结构线性表
    语雀链接:https://www.yuque.com/g/wushi-ls7km/ga9rkw/vrhdf9bfkmshpzus/collaborator/join?token=C3AlDSf6fePw1XfO&source=doc_collaborator#《数据结构线性表》......
  • 数据结构期末复习绪论部分
    语雀链接:https://www.yuque.com/g/wushi-ls7km/ga9rkw/atvszq3vugrzblr0/collaborator/join?token=MY21l2k2LPLrQF8l&source=doc_collaborator#《数据结构期末复习绪论部分》......
  • Educational Codeforces Round 162 (Rated for Div. 2) - VP记录
    Preface这次状态不是很好,B题做的稍微久了一点。D题一眼秒出做法,可惜很多细节没考虑到导致WA了三发。下次应当在做题的时候多多考虑细节,手推数据。而不要想着撞大运撞对。A.MovingChips每一次移动可以补齐一个\(0\),所以找\(1\)中穿插的\(0\)的数量即可,注意两边的\(......
  • Regex Golf通关记录(14)——完结篇
    RegexGolf网址:https://alf.nu/RegexGolfRegexGolf通关解答:RegexGolf通关解答-CSDN博客Quine–不愧是守关的最后一题,当之无愧。先来说说RegexGolf网站的一个“漏洞”,那就是几乎所有的题目,你可能得不出一个优雅的答案,但你至少可以把左侧所有的字符串连接起来,得到一个可......
  • 一些和ChatGPT的沟通记录总结和乱七八糟——(主要是最近确实思路受限,干脆整理一下以前
    现在是2024年11月4日,14:25,最近思路出现了问题,想的几个idea最终都被自己给否了,然后目前有点没有思路的感觉,所以回顾一下之前和ChatGPT的沟通记录以及之前的思路记录。最近心情和整体状态也静下来了,恭喜我,进入工作状态。1.数据的异质性:从去年就提到过异质性,但是一直没持续的研究......
  • mysql服务器上用mysqldump进行数据结构与数据备份
    以下是一个示例命令,它将进行完整的备份并禁用GTIDs:bash mysqldump-uyourusername-p--all-databases--triggers--routines--events--set-gtid-purged=OFF>/path/to/your/complete_dump.sql请将yourusername替换为您的MySQL用户名,/path/to/your/complete_dump.sql......
  • 数据结构 ——— 链式二叉树的前中后序遍历递归实现
    目录前言链式二叉树示意图​编辑手搓一个链式二叉树链式二叉树的前序遍历链式二叉树的中序遍历链式二叉树的后序遍历 前言在上一章学习了链式二叉树的前中后序遍历的解析数据结构———链式二叉树的前中后序遍历解析-CSDN博客接下来要学习的是代码实现链式二叉树......