题目大意
原题链接:https://www.luogu.com.cn/problem/P14598
题解:https://www.luogu.com.cn/article/f8ame8nw
给你一个 01 序列,然后对于一个点,它只能从前面选一个与它不同的点连接。问最少有几条链在一个区间内。\(q\) 个询问,问一段区间 \((l,r)\) 的最小值。
题目分析
首先贪心的想,对于肯定是能配对的配对。就是一个点只能连向前面最近的异点。
证明:
在一个区间内,如果要把原先的两个链给掰开,那么贡献最多是:\(ans=ans-1+1\)。不会更优。
解析都在代码里。
CODE
因为不会树桩数组太爱线段树了,于是打了一坨线段树。谨慎观看,主要看一下思路就行了。
#include<bits/stdc++.h>
#define wk(x) write(x),putchar(' ')
#define wh(x) write(x),putchar('\n')
#define L (p<<1)
#define R (L|1)
#define MID ((l+r)>>1)
#define N 200005
#define ll long long
using namespace std;
int n,m,k,jk,ans,sum,num,cnt,tot;
int kis[2][N],dis[N],lst[N],wis[N],q[N];
char a[N];int read(int &x){x=0;int ff=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){ff=(ch=='-'?-1:ff);ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}x*=ff;return x;
}void write(int x){if(x<0){x=-x;putchar('-');}if(x>=10) write(x/10);putchar('0'+(x%10));return;
}struct SEG{struct T{int l,r,ans;}T[N<<2];void G(int p){T[p].ans=T[L].ans+T[R].ans;return;}void build(int p,int l,int r){T[p].l=l;T[p].r=r;if(l==r){T[p].ans=0;return;}build(L,l,MID),build(R,MID+1,r);G(p);return;}int qry(int p,int l,int r,int x,int y){if(x<=l&&r<=y) return T[p].ans;int z=0;if(MID>=x) z+=qry(L,l,MID,x,y);if(MID<y) z+=qry(R,MID+1,r,x,y);G(p);return z;}void cge(int p,int l,int r,int x,int z){if(l==r){T[p].ans+=z;return;}if(MID>=x) cge(L,l,MID,x,z);else cge(R,MID+1,r,x,z);G(p);return;}
}T;
//线段树维护区间和
//当前每一种颜色最后一次出现的位置权值唯一struct P{int l,r,z,id;
}v[N];bool cmp(P a,P b){return a.r<b.r||(a.r==b.r&&a.l>b.l);
}//按区间(第一关键字以右端点)端点排bool cmp1(P a,P b){return a.id<b.id;
}//按询问顺序来排signed main(){read(n),read(m);scanf("%s",a+1);T.build(1,1,n);//建树for(int i=1;i<=n;i++){int x=a[i]=='P'?1:0;//转化成01序列//kis[x][0] 当前所有链中以x(0/1)为结尾的链的个数。kis[x][1]同理。//kis[x][y] 当前所有链中以x(0/1)为结尾的第i个链的颜色。//因为一定要不同才能连接,故有x^1if(kis[x^1][0]){//前面有满足可链接的情况wis[i]=kis[x^1][kis[x^1][0]];//随便选一个点来连接,我这里用了最后一个点,方便维护kis[x^1][0]--;kis[x][0]++;kis[x][kis[x][0]]=wis[i];//将贡献转换}else{//没有可匹配的点wis[i]=kis[x][++kis[x][0]]=++cnt;//新开一条链}}for(int i=1;i<=m;i++) read(v[i].l) ,read(v[i].r),v[i].id=i;sort(1+v,1+v+m,cmp);int l=1;//排序for(int i=1;i<=n;i++){if(lst[wis[i]]) T.cge(1,1,n,lst[wis[i]],-1);T.cge(1,1,n,i,1);lst[wis[i]]=i;//更新每种颜色的最后位置//考虑为什么这样只是对的:对于每种颜色记录最后的位置,那么就不会有重复算的情况。一个颜色在区间内存在,那么这种颜色的最后一个位置也一定就在这个区间内。但是如果区间前也有,但如果只算最后一个,就不会重复减去贡献。while(v[l].r==i){//已经排好序了,按顺序遍历v[l].z=T.qry(1,1,n,v[l].l,v[l].r);l++;//枚举询问}}sort(1+v,1+v+m,cmp1);for(int i=1;i<=m;i++) wh(v[i].z);return 0;
}