杭电2022 2

1001 Static Query on Tree

转化题意之后就是问一棵树有多少点满足是集合\(A\)中某个节点的祖先且是\(B\)中某个节点的祖先且是\(C\)中某个节点的后代。
\(|A|+|B|+|C|<=10^5\)

满足条件的点一定构成一条链,并且链上最浅的节点是\(C\)中节点最深的节点是\(A\)中一点与\(B\)中一点的\(LCA\)。建出虚树,虚树上一定包含这条链的最浅节点和最深节点,在虚树上\(DP\)即可统计答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=2e5+500;
int read(){
	int sum=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return sum*f;
}
struct edge{
	int to,nxt;
}e[N];
int head[N],cnt;
void add_edge(int u,int v){
	cnt++;
	e[cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int dep[N],dfn[N],root,f[N][22],tot;
void get_dfn(int u){
	for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];
	dfn[u]=++tot;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		dep[v]=dep[u]+1;
		f[v][0]=u;
		get_dfn(v);
	}
}
int get_lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)
		if(f[x][i]!=0&&(f[x][i]!=f[y][i]))x=f[x][i],y=f[y][i];
	return f[x][0];
}
bool cmp(int a,int b){
	return dfn[a]<dfn[b];
}
int stack[N],num[N][4],a[N],top,Map[N],_Map[N],c[4][N],col[N][4],ans;
void build_tree(){
    a[0]=1;a[1]=root;
    for(int i=1;i<=3;i++)
        for(int j=1;j<=c[i][0];j++)
            a[++a[0]]=c[i][j];
    sort(a+1,a+1+a[0],cmp); 
    a[0]=unique(a+1,a+1+a[0])-a-1;
    //处理a[] 
    stack[top=1]=root;
    Map[++tot]=root,_Map[root]=tot;
    //Map[]是虚树到实际树的映射 _Map[]是反映射 
    int x,y;
    for(int i=2;i<=a[0];i++){
        int p=a[i];
        x=stack[top],y=stack[top-1];
        int lca=get_lca(x,p);
        if(lca==x)stack[++top]=p,Map[++tot]=p,_Map[p]=tot;
        else{
            while(top>1&&dfn[y]>=dfn[lca]){
                add_edge(_Map[y],_Map[x]);
                top--;
        		x=stack[top],y=stack[top-1];
            }
            if(x!=lca){
                Map[++tot]=lca;_Map[lca]=tot;
                add_edge(_Map[lca],_Map[x]);
                stack[top]=lca;
            }
            stack[++top]=p;
            Map[++tot]=p,_Map[p]=tot;
        }
    }
    while(top>1){
		x=stack[top],y=stack[top-1];
		add_edge(_Map[y],_Map[x]),top--;
	}
}
void dfs(int u){
	if(col[Map[u]][3])num[u][3]++;
	if(col[Map[u]][1])num[u][1]++;
	if(col[Map[u]][2])num[u][2]++; 
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		num[v][3]+=num[u][3];
		dfs(v);
		num[u][1]+=num[v][1];
		num[u][2]+=num[v][2];
	}
	if(num[u][1]&&num[u][2]&&num[u][3])ans++;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(num[u][1]&&num[u][2]&&num[u][3]&&num[v][1]&&num[v][2]&&num[v][3]){
			ans+=dep[Map[v]]-dep[Map[u]]-1;
		}
	}
}
int main(){
	int T=read();
	while(T--){
		int n=read();
		int q=read();
		for(int i=1;i<n;i++){
			int u=read();
			add_edge(u,i+1);
		}
		root=1;
		dep[root]=1;
		get_dfn(root);
		cnt=0;tot=0;
		for(int i=1;i<=n;i++)head[i]=0;
		while(q--){
			c[1][0]=read(),c[2][0]=read(),c[3][0]=read();
			for(int i=1;i<=3;i++)
				for(int j=1;j<=c[i][0];j++){
					c[i][j]=read();
					col[c[i][j]][i]=1;
				} 
			build_tree();
			ans=0;
			dfs(_Map[root]);
			printf("%d\n",ans);
			for(int i=1;i<=3;i++)
				for(int j=1;j<=c[i][0];j++)
					col[c[i][j]][i]=0;
			cnt=0;
			for(int i=1;i<=tot;i++)head[i]=0,num[i][1]=num[i][2]=num[i][3]=0;
			tot=0;
		}
	} 
	return 0;
} 

1005 Slayers Come

\(m\)个技能,每一个技能可以干掉位置\(pos_i\)上的怪兽并秽土转生它使它的攻击力下降\(l_i\)/\(r_i\)后攻击它相邻的怪兽。问有多少技能集合可以使得使用集合中所有技能后所有\(n\)个怪兽都至少死过一次。

可以预处理出每一个技能的覆盖范围,然后问题转化成了选择若干个区间覆盖整个区间的计数。

求出数组\(c[i]=a[i+1]-b[i]\)一个技能的覆盖区间的左边界就是\(c[pos[i]-1]到c[1]\)中第一个小于\(l_i\)的下标+1。这个东西可以用平衡树求出,用同样的方法也可以求出区间的右边界。

剩下的计数可以用\(DP\)解决

\(dp[i]\)表示\(1~i\)被覆盖\(i+1\)不被覆盖的方案数。

将区间按右端点排序之后枚举区间

转移就是 \(dp[r]=\sum_{i=l-1}^{r}dp[i], \ dp[i]*=2 (i<=l-2)\)

转移的过程可以用线段树优化。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<cstdlib>
using namespace std;
#define int long long
#define mid (L+R>>1)
#define ls (now<<1)
#define rs (now<<1|1)
const int p=998244353;
const int INF=1e9+7;
const int N=5e5+50;
int a[N],b[N],pos[N],l[N],r[N],root,x,y;
int read(){
	int sum=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar(); }
	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return sum*f;
}
int tot,val[N],mn[N],rad[N],ch[N][2],W[N];
int new_node(int x,int w){
	int now=++tot;
	val[now]=x;
	mn[now]=w;
	W[now]=w;
	rad[now]=rand();
	return now;
} 
void update(int now){
	mn[now]=W[now];
	if(ch[now][0])mn[now]=min(mn[now],mn[ch[now][0]]);
	if(ch[now][1])mn[now]=min(mn[now],mn[ch[now][1]]);
}
int merge(int x,int y){
	if(x==0||y==0)return x+y;
	if(rad[x]>rad[y]){
		ch[x][1]=merge(ch[x][1],y);
		update(x);
		return x; 
	}
	else{
		ch[y][0]=merge(x,ch[y][0]);
		update(y);
		return y;
	}
}
void split(int now,int &x,int &y,int w){
	if(now==0){
		x=y=0;
		return;
	}
	if(val[now]<=w){
		x=now;
		split(ch[now][1],ch[x][1],y,w);
	}
	else{
		y=now;
		split(ch[now][0],x,ch[y][0],w);
	}
	update(now);
}
int find_1(int w){
	int now=x,ans=-INF;
	while(now){
		if(W[now]<w)ans=max(ans,now);
		if(mn[ch[now][1]]<w&&ch[now][1])now=ch[now][1];
		else now=ch[now][0];
	}
	return val[ans];
}
int find_2(int w){
	int now=y,ans=INF;
	while(now){
		if(W[now]<w)ans=min(ans,now);
		if(mn[ch[now][0]]<w&&ch[now][0])now=ch[now][0];
		else now=ch[now][1];
	}
	return val[ans];
}
void clear(){
	for(int i=1;i<=tot;i++)ch[i][0]=ch[i][1]=mn[i]=val[i]=rad[i]=W[i]=0;
	tot=0;root=0;
}
struct line{
	int l,r;
}c[N];
bool cmp(line a,line b){
	return a.r<b.r;
}
int sum[N*8],lazy_1[N*8],lazy_2[N*8];
void update_(int now){
	sum[now]=(sum[ls]+sum[rs])%p; 
}
void init(int L,int R,int now){
	lazy_1[now]=1,lazy_2[now]=0;
	sum[now]=0;
	if(L==R){
		if(L==1)sum[now]=1;
		return;
	}
	init(L,mid,ls);
	init(mid+1,R,rs);
	update_(now);
}
void pushdown(int L,int R,int now){
	if(lazy_1[now]!=1){
		lazy_1[ls]=lazy_1[ls]*lazy_1[now]%p;
		lazy_2[ls]=lazy_2[ls]*lazy_1[now]%p;
		sum[ls]=sum[ls]*lazy_1[now]%p;
		lazy_1[rs]=lazy_1[rs]*lazy_1[now]%p;
		lazy_2[rs]=lazy_2[rs]*lazy_1[now]%p;
		sum[rs]=sum[rs]*lazy_1[now]%p;
		lazy_1[now]=1;
	}
	if(lazy_2[now]){
		lazy_2[ls]=(lazy_2[ls]+lazy_2[now])%p;
		sum[ls]=(sum[ls]+(mid-L+1)*lazy_2[now])%p;
		lazy_2[rs]=(lazy_2[rs]+lazy_2[now])%p;
		sum[rs]=(sum[rs]+(R-mid)*lazy_2[now])%p;
		lazy_2[now]=0;
	}
}
int get_sum(int L,int R,int l,int r,int now){
	pushdown(L,R,now);
	if(L==l&&R==r)return sum[now];
	if(l>mid)return get_sum(mid+1,R,l,r,rs);
	else if(r<=mid)return get_sum(L,mid,l,r,ls);
	else return (get_sum(L,mid,l,mid,ls)+get_sum(mid+1,R,mid+1,r,rs))%p; 
}
void add(int L,int R,int x,int w,int now){
	pushdown(L,R,now);
	if(L==R){
		sum[now]=(sum[now]+w)%p;
		return;
	}
	if(x>mid)add(mid+1,R,x,w,rs);
	else add(L,mid,x,w,ls);
	update_(now);
} 
void mul(int L,int R,int l,int r,int w,int now){
	if(l>r)return; 
	pushdown(L,R,now);
	if(L==l&&R==r){
		sum[now]=sum[now]*w%p;
		lazy_1[now]=lazy_1[now]*w%p;
		lazy_2[now]=lazy_2[now]*w%p;
		return;
	}
	if(l>mid)mul(mid+1,R,l,r,w,rs);
	else if(r<=mid)mul(L,mid,l,r,w,ls);
	else mul(L,mid,l,mid,w,ls),mul(mid+1,R,mid+1,r,w,rs);
	update_(now);
}
signed main(){
	srand(time(NULL));
	int T=read();
	while(T--){
		int n=read(),m=read();
		for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
		for(int i=1;i<=m;i++)pos[i]=read(),l[i]=read(),r[i]=read();
		for(int i=1;i<n;i++)root=merge(root,new_node(i,a[i+1]-b[i]));
		for(int i=1;i<=m;i++){
			split(root,x,y,pos[i]-1);
			if(mn[x]>=l[i]||x==0)c[i].l=1;
			else c[i].l=find_1(l[i])+1;
			root=merge(x,y);
		}
		clear();
		for(int i=2;i<=n;i++)root=merge(root,new_node(i,a[i-1]-b[i]));
		for(int i=1;i<=m;i++){
			split(root,x,y,pos[i]);
			if(mn[y]>=r[i]||y==0)c[i].r=n;
			else c[i].r=find_2(r[i])-1;
			root=merge(x,y);
		}
		clear();
		sort(c+1,c+1+m,cmp);
		init(1,n+1,1);
		for(int i=1;i<=m;i++){
			add(1,n+1,c[i].r+1,get_sum(1,n+1,c[i].l,c[i].r+1,1),1);
			mul(1,n+1,1,c[i].l-1,2,1);
		}
		printf("%lld\n",get_sum(1,n+1,n+1,n+1,1));
	}
	return 0;
} 
posted @ 2022-08-01 15:15  Xu-daxia  阅读(21)  评论(0编辑  收藏  举报