决斗(线段树)

青蛙哥与名侦探柯南正在进行一场对决。他们两个人每人有 n n n 张牌,每张牌有一个点数。

并且在接下来的 n n n 个回合中每回合青蛙哥与名侦探柯南两人会各自打出一张牌。

每回合裁判会检查,打出的牌点数更高的一方获胜从而得到一分,如果二人点数相同,则不得分。

然而现在青蛙哥通过偷看的方法得到了名侦探柯南的出牌顺序,他可以任意定一个自己的出牌的顺序。

他首先希望让自己的得分尽可能高,然后就是希望在让自己的得分尽可能高这个前提下,最大化自己从第一回合开始到最后一个回合结束过程中,每回合出牌点数构成的字符串的字典序。

1 ≤ n , a i , b i ≤ 1 0 5 1\le n,a_i,b_i\le10^5 1n,ai,bi105


假设我们已经求出青蛙最多得 k k k 分,那么下面按顺序考虑填入什么数字能使字典序最大。

对于第 i i i 回合,如果我们要赢,可以出最大的,但是如果这么做可能会影响后面需要它来赢的回合;如果我们要输,可以出最小的,但是字典序就不一定最大。容易得到出牌点数在一个区间 [ l , r ] [l,r] [l,r] 内。

这时可以二分求出我们能填的最大点数,然后判断出这张是否会影响得分。

判断的实现可以用线段树。建权值线段树,每个节点维护三元组 ( s , s 1 , s 2 ) (s,s_1,s_2) (s,s1,s2),分别表示 当前值域的得分、柯南剩的牌数、青蛙剩的牌数。在向上合并信息时,青蛙可以用自己的大牌去对柯南的小牌,青蛙的得分是 R s 2 − L s 1 R_{s_2}-L_{s_1} Rs2Ls1,就是用右区间青蛙牌数减去左区间柯南牌数。 s 1 , s 2 s_1,s_2 s1,s2 的维护显然。

具体实现参照代码。

时间复杂度 O ( n log ⁡ 2 V ) O(n\log^2V) O(nlog2V),其中 V V V 是值域。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
int n,a[N],b[N];
multiset<int> s;
struct node
{
   
    int s,s1,s2;//a b
}tr[N<<2];
void pushup(int rt)
{
   
    int x=min(tr[rt<<1|1].s2,tr[rt<<1].s1);
    tr[rt].s=tr[rt<<1].s+tr[rt<<1|1].s+x;
    tr[rt].s1=tr[rt<<1].s1+tr[rt<<1|1].s1-x;
    tr[rt].s2=tr[rt<<1].s2+tr[rt<<1|1].s2-x;
}
void update(int rt,int l,int r,int x,int d1,int d2)
{
   
    if(l==r){
   
        tr[rt].s1+=d1;
        tr[rt].s2+=d2;
        return;
    }
    int mid=l+r>>1;
    if(x<=mid) update(rt<<1,l,mid,x,d1,d2);
    else update(rt<<1|1,mid+1,r,x,d1,d2);
    pushup(rt);
}
int main()
{
   
    // freopen("duel.in","r",stdin);
    // freopen("duel.out","w",stdout);
    cin.tie(0)->sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],update(1,1,100000,a[i],1,0);
    for(int i=1;i<=n;i++) cin>>b[i],update(1,1,100000,b[i],0,1),s.insert(b[i]);
    int ans=tr[1].s;//剩余回合需得分数
    for(int i=1;i<=n;i++){
   
        update(1,1,100000,a[i],-1,0);//删去ai,如果不会对得分有影响,tr[1].s恒为ans,即二分不起作用
        int l=a[i]+1,r=*s.rbegin(),Ans;
        while(l<=r){
   
            int mid=l+r>>1;
            update(1,1,100000,mid,0,-1);
            if(tr[1].s+1==ans) l=mid+1,Ans=mid;//如果有影响分数,就尝试提高点数
            else r=mid-1;
            update(1,1,100000,mid,0,1);
        }
        update(1,1,100000,Ans,0,-1);
        if(a[i]<*s.rbegin()&&tr[1].s+1==ans){
   //二分的答案Ans合法,此回合得分
            ans--;
            cout<<Ans<<" ";
            s.erase(s.find(Ans));
        }
        else{
   //此回合不能得分
            update(1,1,100000,Ans,0,1);
            l=1,r=a[i],Ans=l;//青蛙的牌不超过ai
            while(l<=r){
   
                int mid=l+r>>1;
                update(1,1,100000,mid,0,-1);
                if(tr[1].s==ans) l=mid+1,Ans=mid;//如果不影响分数,就尝试提高点数
                else r=mid-1;
                update(1,1,100000,mid,0,1);
            }
            update(1,1,100000,Ans,0,-1);
            cout<<Ans<<" ";
            s.erase(s.find(Ans));
        }
    }
}

相关推荐

  1. 决斗线段

    2024-01-23 03:58:02       53 阅读
  2. 风信子(线段

    2024-01-23 03:58:02       53 阅读
  3. 【C++】线段(一)

    2024-01-23 03:58:02       60 阅读
  4. 线段CF 练习题

    2024-01-23 03:58:02       46 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-23 03:58:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-23 03:58:02       101 阅读
  3. 在Django里面运行非项目文件

    2024-01-23 03:58:02       82 阅读
  4. Python语言-面向对象

    2024-01-23 03:58:02       91 阅读

热门阅读

  1. Quarkus 2.8.0引入了细粒度的Transaction API

    2024-01-23 03:58:02       58 阅读
  2. MySQL索引优化:深入理解索引合并

    2024-01-23 03:58:02       53 阅读
  3. Android扫码方案

    2024-01-23 03:58:02       53 阅读
  4. Vue中的模式和环境变量

    2024-01-23 03:58:02       52 阅读
  5. 各行业领域向chatgpt高质量提问的方式

    2024-01-23 03:58:02       52 阅读
  6. docker

    2024-01-23 03:58:02       50 阅读
  7. 数据结构_复杂度+之后的事-1.18

    2024-01-23 03:58:02       48 阅读
  8. 服务器端口被占用怎么解决

    2024-01-23 03:58:02       57 阅读
  9. ❤ vue的实际使用

    2024-01-23 03:58:02       57 阅读