杂题笔记

CF2133F Flint and Steel

首先把每个能爆炸的苦力怕爆炸极限跑出来,配合苦力怕位置(核心)组一个结构体

注意到爆炸序列是合法当且仅当不存在两个被引爆的苦力怕,他们的互相包含对方的核心

那么相邻两个苦力怕存在三种合法情况,前者包含后者,后者包含前者,互不包含

\(f_i\) 为炸到 \(i\) 所需的最少苦力怕数,那么三种转移分两种优化转移:前者不包含后者,前者包含后者且后者不包含前者

各维护一棵线段树,第一种在跑到爆炸极限右端点时更新,然后在每个核心处转移。
第二种在核心处更新,在爆炸极限左端点处转移。
先转移后更新

同时为了保证合法性,进行右端点单修而非区修,防止严格更弱的区间夹在中间变出合法方案

#include<bits/stdc++.h>
#define N 500005
#define inf 100000000
using namespace std;
int x[N],L[N],R[N],num[N];
vector<int>pre[N],suf[N];
struct Ty{int u,val;}fa[N],dp[N];
Ty cmp(Ty a,Ty b){
	if(a.val<b.val)return a;
	return b;
}
bool over(int a,int b){return x[a]<x[b];}
struct Ig{
	Ty y[N<<2];
	void build(int u,int l,int r){
		y[u].val=inf;
		y[u].u=0;
		if(l==r)return;
		int mid=(l+r)/2;
		build(u*2,l,mid);
		build(u*2+1,mid+1,r);
		return;
	}
	void update(int u,int l,int r,int id,Ty val){
		if(l==r&&l==id){
			y[u]=cmp(val,y[u]);
			return;
		}
		int mid=(l+r)/2;
		if(id<=mid)update(u*2,l,mid,id,val);
		else update(u*2+1,mid+1,r,id,val);
		y[u]=cmp(y[u*2],y[u*2+1]);
		return;
	}
	Ty query(int u,int l,int r,int fl,int fr){
		if(l==fl&&r==fr)return y[u];
		int mid=(l+r)/2;
		if(fr<=mid)return query(u*2,l,mid,fl,fr);
		else if(fl>mid)return query(u*2+1,mid+1,r,fl,fr);
		else return cmp(query(u*2,l,mid,fl,mid),query(u*2+1,mid+1,r,mid+1,fr));
	}
}T1,T2;
signed main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int n;
		Ty ans={0,inf};
		scanf("%d",&n);
		for(int i=1;i<=n;i++)fa[i]=dp[i]={0,inf};
		for(int i=1;i<=n;i++)scanf("%d",&x[i]);
		T1.build(1,1,n);
		T2.build(1,1,n);
		for(int i=1;i<=n;i++)if(x[i]>0){
			L[i]=max(1,i-x[i]+1);
			R[i]=min(n,i+x[i]-1);
			pre[L[i]].push_back(i);
			suf[R[i]].push_back(i);
		}
		for(int i=1;i<=n;i++){
			for(int j=0;j<pre[i].size();j++){
				if(i==1)fa[pre[i][j]]={0,0};
				else fa[pre[i][j]]=T2.query(1,1,n,max(1,i-1),R[pre[i][j]]-1);
			}
			if(x[i]>0){
				fa[i]=cmp(fa[i],T1.query(1,1,n,max(1,L[i]-1),R[i]));
				dp[i].u=i;
				dp[i].val=fa[i].val+1;
				T2.update(1,1,n,R[i],dp[i]);
			}
			for(int j=0;j<suf[i].size();j++){
				if(i==n)ans=cmp(ans,dp[suf[i][j]]);
				else T1.update(1,1,n,i,dp[suf[i][j]]);
			}
		}
		if(ans.val>1e7){
			printf("-1\n");
			for(int i=1;i<=n;i++){
				L[i]=R[i]=0;
				pre[i].clear();
				suf[i].clear();
			}
			continue;
		}
		printf("%d\n",ans.val);
		int top=0;
		int u=ans.u;
		while(u){
			num[++top]=u;
			u=fa[u].u;
		}
		sort(num+1,num+top+1,over);
		for(int i=1;i<=top;i++)printf("%d ",num[i]);
		printf("\n");
		for(int i=1;i<=n;i++){
			L[i]=R[i]=0;
			pre[i].clear();
			suf[i].clear();
		}
	}
	return 0;
}

CF2147F Exchange Queries

第一步转化:连出所有 \(p\to s,p\to p-1,s\to s-1\) 的边,那么问题就会转化成强联通缩点DAG图上可达点总数问题

第二步转化:注意到对于一条边 \((p_i,s_i)\),如果在强联通分量中存在 \(j\) 满足 \((p_i<p_j)+(s_i<s_j)=1\),那么边 \(i\)就可以合并进去,所以跑出来的 DAG 最多只有一个直接父节点和一个直接子节点,说白了就是条链

第三步转化:这玩意可以具象为两排点,对于每个 \(i\) 从上排点 \(p_i\) 向下排点 \(s_i\) 连边,那么每个强连通分量就是极大相交线段集合

第四步转化:推导一下可以发现每个极大相交线段集合包含的上下排点都正好填满一个连续段,那么只需要求出每个连续段的右端点即可

维护:显然的,对于边 \((p_i,s_i)\)\(p_i\sim s_i-1\) 都不能成为合法右端点,那么我们可以对这部分区间 +1,那么右端点集就是 0 构成的集合,在线段树上标记永久化再加上维护区间内答案,区间左右极值 0 就可以做了

#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
struct Ty{int u,v,id;}x[N];
struct Ig{int tag,val,l,r;}y[N*4];
void build(int u,int l,int r){
    y[u].val=(r-l)*(l+r+1)/2;
    y[u].tag=0;
    y[u].l=l;
    y[u].r=r;
    if(l==r)return;
    int mid=(l+r)/2;
    build(u*2,l,mid);
    build(u*2+1,mid+1,r);
    return;
}
void update(int u,int l,int r,int fl,int fr,int val){
    if(l==fl&&r==fr){
        y[u].tag+=val;
        if(y[u].tag)y[u].val=y[u].l=y[u].r=0;
        else if(l==r){
            y[u].val=0;
            y[u].l=y[u].r=l;
        }
        else{
            y[u].val=y[u*2].val+y[u*2+1].val;
            y[u].l=y[u*2].l?y[u*2].l:y[u*2+1].l;
            y[u].r=y[u*2+1].r?y[u*2+1].r:y[u*2].r;
            if(y[u*2].r&&y[u*2+1].l)y[u].val+=(y[u*2+1].l-y[u*2].r)*y[u*2+1].l;
        }
        return;
    }
    int mid=(l+r)/2;
    if(fr<=mid)update(u*2,l,mid,fl,fr,val);
    else if(fl>mid)update(u*2+1,mid+1,r,fl,fr,val);
    else{
        update(u*2,l,mid,fl,mid,val);
        update(u*2+1,mid+1,r,mid+1,fr,val);
    }
    if(y[u].tag)y[u].val=y[u].l=y[u].r=0;
    else{
        y[u].val=y[u*2].val+y[u*2+1].val;
        y[u].l=y[u*2].l?y[u*2].l:y[u*2+1].l;
        y[u].r=y[u*2+1].r?y[u*2+1].r:y[u*2].r;
        if(y[u*2].r&&y[u*2+1].l)y[u].val+=(y[u*2+1].l-y[u*2].r)*y[u*2+1].l;
    }
    return;
}
signed main(){
    int T;
    scanf("%lld",&T);
    while(T--){
        int n,q;
        scanf("%lld%lld",&n,&q);
        for(int i=1;i<=n;i++)scanf("%lld",&x[i].u);
        for(int i=1;i<=n;i++)scanf("%lld",&x[i].v);
        build(1,1,n);
        for(int i=1;i<=n;i++)if(x[i].u<x[i].v)update(1,1,n,x[i].u,x[i].v-1,1);
        while(q--){
            int op;
            scanf("%lld",&op);
            if(op==1){
                int u,v;
                scanf("%lld%lld",&u,&v);
                if(x[u].u<x[u].v)update(1,1,n,x[u].u,x[u].v-1,-1);
                if(x[v].u<x[v].v)update(1,1,n,x[v].u,x[v].v-1,-1);
                swap(x[u].u,x[v].u);
                if(x[u].u<x[u].v)update(1,1,n,x[u].u,x[u].v-1,1);
                if(x[v].u<x[v].v)update(1,1,n,x[v].u,x[v].v-1,1);
            }
            else{
                int u,v;
                scanf("%lld%lld",&u,&v);
                if(x[u].u<x[u].v)update(1,1,n,x[u].u,x[u].v-1,-1);
                if(x[v].u<x[v].v)update(1,1,n,x[v].u,x[v].v-1,-1);
                swap(x[u].v,x[v].v);
                if(x[u].u<x[u].v)update(1,1,n,x[u].u,x[u].v-1,1);
                if(x[v].u<x[v].v)update(1,1,n,x[v].u,x[v].v-1,1);
            }
            printf("%lld\n",y[1].val+y[1].l*y[1].l);
        }
    }
    return 0;
}
posted @ 2025-09-28 17:55  Igunareo  阅读(11)  评论(0)    收藏  举报