题目
NNN张牌,每张牌有AAA、BBB面。AAA面的值为A[i],BBB面的值为B[i]。初始AAA面朝上。
有KKK次操作,每次操作给出一个数T[i],把当前值小于或等于T[i]的牌翻面
求KKK次操作后,所有牌面向上的面值之和。
题解
前几天主席树学疯了…
考试的时候有这样想过,但直觉告诉我这是一道主席树的题…
感觉自己傻的不行
说明有时候换角度思考问题是很有必要的…
先考虑Ai<BiA_i<B_iAi?<Bi?的情况,Ai>BiA_i>B_iAi?>Bi?同理
若Tj<AiT_j<A_iTj?<Ai?显然这次操作不会改变第iii号牌。
若Ai≤Tj<BiA_i≤T_j<B_iAi?≤Tj?<Bi?这次操作会让这张牌一定变成BiB_iBi?。
若Bi≤TjB_i≤T_jBi?≤Tj?这次操作会让这张牌正常的交换。
然后神仙操作来了:找到最后一次Ai≤Tj<BiA_i≤T_j<B_iAi?≤Tj?<Bi?操作,再统计之后Bi≤TjB_i≤T_jBi?≤Tj?的操作,就可以直接推出这张牌的正反面。
显然是一个线段树加树状数组或者cdq分治能够做到的事情。
具体来说,就是先将A,B,TA,B,TA,B,T都离散化,然后将TjT_jTj?插入到线段树里。维护一下下标jjj的区间最大值。
然后询问一下[min(Ai,Bi),max(Ai,Bi)?1][min(A_i,B_i),max(A_i,B_i)-1][min(Ai?,Bi?),max(Ai?,Bi?)?1]就能够找到最后一次使Ai≤Tj<BiA_i≤T_j<B_iAi?≤Tj?<Bi?的位置PiP_iPi?。
这样的话就是找到满足Pi<j,max(Ai,Bi)?1<TjP_i<j,max(A_i,B_i)-1<T_jPi?<j,max(Ai?,Bi?)?1<Tj?的jjj。
将它看作两个点(Pi,max(Ai,Bi)?1)(P_i,max(A_i,B_i)-1)(Pi?,max(Ai?,Bi?)?1)和(j,Tj)(j,T_j)(j,Tj?)。
就可以愉快的二维数点了
时间复杂度:O(nlog?n)O(n\log n)O(nlogn)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#include<vector>
using namespace std;
#define MAXN 200000
#define LL long long
#define INF 1000000000
#define MOD 1000000000
#define PB push_back
#define MP make_pair
#define FR first
#define SE second
#define Mid (l+r)/2
int n,k;
int A[MAXN+5],B[MAXN+5],T[MAXN+5];
int pcnt,X[MAXN*3+5],f[MAXN*3+5];
int maxx[MAXN*12+5],ans[MAXN*3+5];
struct node{
int num,x,y;
}a[MAXN*3+5],tmp[MAXN*3+5];
bool cmp(node s1,node s2){
return s1.x<s2.x;}
int read()
{
int f=1,x=0;char c=getchar();while(c<'0'||c>'9'){
if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){
x=x*10+c-'0';c=getchar();}return x*f;
}
void Insert(int x,int l,int r,int pos,int val)
{
if(l==r){
maxx[x]=max(maxx[x],val);return ;}if(pos<=Mid)Insert(x<<1,l,Mid,pos,val);else Insert(x<<1|1,Mid+1,r,pos,val);maxx[x]=max(maxx[x<<1],maxx[x<<1|1]);
}
int Query(int x,int l,int r,int L,int R)
{
if(r<L||R<l)return 0;if(L<=l&&R>=r)return maxx[x];return max(Query(x<<1,l,Mid,L,R),Query(x<<1|1,Mid+1,r,L,R));
}
void CDQ(int l,int r)
{
if(l==r)return ;CDQ(l,Mid),CDQ(Mid+1,r);int pl=l,pr=Mid+1,p=l,cnt=0;for(int i=Mid+1;i<=r;i++)if(a[i].num==-1)cnt++;while(pl<=Mid&&pr<=r){
if(a[pl].y<a[pr].y){
if(a[pl].num!=-1)ans[a[pl].num]+=cnt;tmp[p++]=a[pl++];}else{
if(a[pr].num==-1)cnt--;tmp[p++]=a[pr++];}}while(pl<=Mid){
if(a[pl].num!=-1)ans[a[pl].num]+=cnt;tmp[p++]=a[pl++];}while(pr<=r)tmp[p++]=a[pr++];for(int i=l;i<=r;i++)a[i]=tmp[i];
}
int main()
{
freopen("fortune.in","r",stdin);freopen("fortune.out","w",stdout);n=read(),k=read();for(int i=1;i<=n;i++)X[++pcnt]=A[i]=read(),X[++pcnt]=B[i]=read();for(int i=1;i<=k;i++)X[++pcnt]=T[i]=read();sort(X+1,X+pcnt+1);pcnt=unique(X+1,X+pcnt+1)-X-1;for(int i=1;i<=n;i++){
A[i]=lower_bound(X+1,X+pcnt+1,A[i])-X;B[i]=lower_bound(X+1,X+pcnt+1,B[i])-X;}for(int i=1;i<=k;i++)T[i]=lower_bound(X+1,X+pcnt+1,T[i])-X;for(int i=1;i<=k;i++)Insert(1,1,pcnt,T[i],i);for(int i=1;i<=n;i++){
f[i]=Query(1,1,pcnt,min(A[i],B[i]),max(A[i],B[i])-1);a[i]=(node){
i,max(A[i],B[i])-1,f[i]};}for(int i=1;i<=k;i++)a[n+i]=(node){
-1,T[i],i};sort(a+1,a+n+k+1,cmp);CDQ(1,n+k);LL res=0;for(int i=1;i<=n;i++){
if(A[i]==B[i])res+=X[A[i]];else if(A[i]<B[i]&&f[i])res+=(ans[i]&1)?X[A[i]]:X[B[i]];else res+=(ans[i]&1)?X[B[i]]:X[A[i]];}printf("%lld",res);
}