传送门:CF
前题提要:作为一个DS选手,在赛时没有解出这道题感觉还是有一点遗憾的.其实感觉在比赛的时候,想到的trick已经和正解很接近了,但是由于一些经验问题,导致脑子里想的不是很清楚.所以最后还是没做出来.
首先对于这道题.我们先来简单分析一下T(S)如何取得最大的字典序.因为我们的字符串只有0/1.所以显然应该尽量的让1放前面才行.我们再来仔细考虑一下这个.我们会发现假设我们的T(s)里面存在两个字符,这两个字符是由S中的同一个位置而来的,那么后面字符的0/1将会由前面的那一个字符来决定.也就是当前面的那一个字符变成1之后,后面的那个字符也会跟着变成1.所以后面的那一个字符我们就可以不用管了.
考虑记录S中每一个字符在T(S)中第一个出现第一个出现的位置.然后按照出现位置的顺序来排序.举一个例子:
假设我们的T(S)是6562131(其中数字代表该字符在S中的位置从1开始),那么我们记录的就是65213,也就是计算出T(S)中第一个出现的字符在S中的位置,第二个,第三个等等.
那么显然的,假设我们想让字典序最大,我们需要尽量的将这些数字从前往后变成1,也就是先满足6为1,再满足5为1,等等,简单来说就是先算出每一个位置变1的优先级,那么我们此时的花费,就是尽量将其变为1的花费.
那么这个花费是多少呢,我们可以算出S中所有1的个数记为K,然后算出T(S)去重后中前K位1的个数,记为K2,那么我们的花费就是K-K2.(当然此时有特例,比如去重后的字符的总个数可能小于K,这个简单特判一下即可,此处不展开).
本题的trick到这里就基本结束了
接下来考虑如何来维护上述需要的所有信息.
首先是如何计算出每一个位置变1的优先级呢.这个可以用很多数据结构来维护,在网上找题解,你会发现大多数人会告诉你这个维护方式很典.(别问为什么我知道,问就是我查过).但是对于本人来说,我致力于讲清楚每一点.(因为说典不如不发表题解)
考虑到网上各大博客题解绝大部分都是并查集,但是并查集维护本题的思想又比较难懂.所以我准备讲讲简单的线段树做法.对于每一个给出的点,我们先维护这个点第一次出现的区间位置.这个维护方式有大致两种,我们可以正着来维护,也可以倒着来维护.正着来维护的话,我们需要维护min值,但是倒着来维护的话,我们只要区间覆盖即可(因为倒着来枚举区间显然后来覆盖的是最优的).我这里选择倒着来维护.这个维护十分简单,如果没有看懂可以看看具体实现的代码,相信你会很快明白的(这个是真简单,不同于并查集做法)
然后对于两个点,假设他们处于不同区间,显然按照区间顺序来排序.假设他们处于同一区间,我们按照点在S中的位置顺序来排序.,此时我们就维护好了每一个点的优先级.
对于上述中的K,我们其实很好维护.因为每一个询问是单点修改,我们直接修改即可.
然后考虑如何维护前缀1的个数.在这里,我们直接使用树状数组来维护单点修改即可.(线段树当然也行,但是码量较大).对于刚开始存在的1,我们先加入树状数组中.然后对于每一次修改,我们在树状数组中进行相应的修改即可.
本题感觉刚开始想还是比较绕的,建议结合代码食用.
下面是具体的代码部分;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Segment_tree{
int l,r,first_pos,lazy;
}tree[maxn<<2];
void build(int l,int r,int rt) {
tree[rt].l=l;tree[rt].r=r;tree[rt].first_pos=int_INF;
if(l==r) {
return ;
}
int mid=(l+r)>>1;
build(lson);build(rson);
}
void change(int rt,int val) {
tree[rt].first_pos=val;tree[rt].lazy=val;
}
void pushdown(int rt) {
change(ls,tree[rt].lazy);change(rs,tree[rt].lazy);
tree[rt].lazy=0;
}
void update(int l,int r,int rt,int val) {
if(tree[rt].l==l&&tree[rt].r==r) {
change(rt,val);
return ;
}
if(tree[rt].lazy) pushdown(rt);
int mid=(tree[rt].l+tree[rt].r)>>1;
if(r<=mid) update(l,r,ls,val);
else if(l>mid) update(l,r,rs,val);
else update(l,mid,ls,val),update(mid+1,r,rs,val);
}
int query(int pos,int rt) {
if(tree[rt].l==pos&&tree[rt].r==pos) {
return tree[rt].first_pos;
}
if(tree[rt].lazy) pushdown(rt);
int mid=(tree[rt].l+tree[rt].r)>>1;
if(pos<=mid) return query(pos,ls);
else return query(pos,rs);
}
int l[maxn],r[maxn];
struct Node{
int id,pos;
}node[maxn];
bool cmp(Node a,Node b) {
return a.pos==b.pos?a.id<b.id:a.pos<b.pos;
}
int Bit_tree[maxn];int n,m,q;
int lowbit(int x) {
return x&(~x+1);
}
int len;
void Add(int pos,int val) {
while(pos<=len) {
Bit_tree[pos]+=val;
pos+=lowbit(pos);
}
}
int Query(int pos) {
int ans=0;
while(pos) {
ans+=Bit_tree[pos];
pos-=lowbit(pos);
}
return ans;
}
int main() {
n=read();m=read();q=read();
string s;cin>>s;
int sum=0;
for(int i=0;i<s.length();i++) sum+=s[i]=='1';
for(int i=1;i<=m;i++) {
l[i]=read();r[i]=read();
}
build(root);
for(int i=m;i>=1;i--) {
update(l[i],r[i],1,i);
}
for(int i=1;i<=n;i++) {
node[i].id=i;node[i].pos=query(i,1);
}
sort(node+1,node+n+1,cmp);
len=n;
while(node[len].pos==int_INF) len--;
map<int,int>mp;
for(int i=1;i<=len;i++) {
Add(i,s[node[i].id-1]=='1');
mp[node[i].id]=i;
}
for(int i=1;i<=q;i++) {
int x=read();
if(s[x-1]=='0') {
s[x-1]='1';sum++;
if(mp[x]!=0) Add(mp[x],1);
cout<<min(sum,len)-Query(min(sum,len))<<endl;
}
else {
s[x-1]='0';sum--;
if(mp[x]!=0) Add(mp[x],-1);
cout<<min(sum,len)-Query(min(sum,len))<<endl;
}
}
return 0;
}