当前位置: 代码迷 >> 综合 >> [JOI Open 2014 day1]Fortune Telling 2(线段树+二维数点)
  详细解决方案

[JOI Open 2014 day1]Fortune Telling 2(线段树+二维数点)

热度:55   发布时间:2023-10-22 22:05:29.0

题目

[JOI Open 2014 day1]Fortune Telling 2(线段树+二维数点)
[JOI Open 2014 day1]Fortune Telling 2(线段树+二维数点)
[JOI Open 2014 day1]Fortune Telling 2(线段树+二维数点)
[JOI Open 2014 day1]Fortune Telling 2(线段树+二维数点)
NNN张牌,每张牌有AAABBB面。AAA面的值为A[i],BBB面的值为B[i]。初始AAA面朝上。
KKK次操作,每次操作给出一个数T[i],把当前值小于或等于T[i]的牌翻面
KKK次操作后,所有牌面向上的面值之和。

题解

前几天主席树学疯了…
考试的时候有这样想过,但直觉告诉我这是一道主席树的题…
感觉自己傻的不行
说明有时候换角度思考问题是很有必要的…

先考虑Ai&lt;BiA_i&lt;B_iAi?<Bi?的情况,Ai&gt;BiA_i&gt;B_iAi?>Bi?同理
Tj&lt;AiT_j&lt;A_iTj?<Ai?显然这次操作不会改变第iii号牌。
Ai≤Tj&lt;BiA_i≤T_j&lt;B_iAi?Tj?<Bi?这次操作会让这张牌一定变成BiB_iBi?
Bi≤TjB_i≤T_jBi?Tj?这次操作会让这张牌正常的交换。
然后神仙操作来了:找到最后一次Ai≤Tj&lt;BiA_i≤T_j&lt;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&lt;BiA_i≤T_j&lt;B_iAi?Tj?<Bi?的位置PiP_iPi?
这样的话就是找到满足Pi&lt;j,max(Ai,Bi)?1&lt;TjP_i&lt;j,max(A_i,B_i)-1&lt;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);
}
  相关解决方案