zoukankan      html  css  js  c++  java
  • [JZOJ6400]:Game(贪心+线段树+二分)

    题目描述

      小$A$和小$B$在玩一个游戏,他们两个人每人有$n$张牌,每张牌有一个点数,并且在接下来的$n$个回合中每回合他们两人会分别打出手中的一张牌,点数严格更高的一方得一分,然而现在小$A$通过某种神秘的方法得到了小$B$的出牌顺序,现在他希望规划自己的出牌顺序使得自己在得分尽可能高的前提下出牌的字典序尽可能大。


    输入格式

      第一行一个正整数$n$表示游戏进行的轮数。
      接下来一行$n$个整数,第$i$个数表示第$i$轮小$B$将要打出的牌的点数。
      接下来一行$n$个整数,表示小$A$拥有的牌的点数。


    输出格式

      输出一行$n$个整数,表示小$A$出牌的顺序。


    样例

    样例输入:

    5
    1 2 3 4 5
    3 2 2 1 4

    样例输出:

    2 3 4 2 1


    数据范围与提示

      对于$20\%$的数据,$nleqslant 10$
      对于$40\%$的数据,$nleqslant 3,000$
      对于$60\%$的数据,$nleqslant 6,000$
      对于$100\%$的数据,$n,a_ileqslant 100,000$


    题解

    考虑贪心,一定是尽量拿小的压他更优。

    这个排个序解决即可。

    考虑如何另字典序最大,试着反着考虑。

    发现答案满足单调性,考虑二分。

    用线段树维护最大得分,然后在每一位上二分,利用贪心找到能够满足答案的最大得分即可。

    时间复杂度:$Theta(nlog n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    #define L(x) x<<1
    #define R(x) x<<1|1
    using namespace std;
    unordered_map<int,int>mp;
    int n;
    int a[100001],b[100001],lc[200001],top,cnt;
    int trns[400001],trsz[400001][2],trrk[400001];
    void pushup(int x)
    {
    	int res=min(trsz[L(x)][0],trsz[R(x)][1]);
    	trns[x]=trns[L(x)]+trns[R(x)]+res;
    	trsz[x][0]=trsz[L(x)][0]+trsz[R(x)][0]-res;
    	trsz[x][1]=trsz[L(x)][1]+trsz[R(x)][1]-res;
    	trrk[x]=trrk[L(x)]+trrk[R(x)];
    }
    void add(int x,int l,int r,int k,int id,int w)
    {
    	if(l==r)
    	{
    		trsz[x][id]+=w;
    		trrk[x]+=id*w;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(k<=mid)add(L(x),l,mid,k,id,w);
    	else add(R(x),mid+1,r,k,id,w);
    	pushup(x);
    }
    int ask(int x,int l,int r,int k)
    {
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	if(k<=trrk[L(x)])return ask(L(x),l,mid,k);
    	else return ask(R(x),mid+1,r,k-trrk[L(x)]);
    }
    int get(int x,int l,int r,int k)
    {
    	if(l==r)return trrk[x];
    	int mid=(l+r)>>1;
    	if(k<=mid)return get(L(x),l,mid,k);
    	else return get(R(x),mid+1,r,k)+trrk[L(x)];
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){scanf("%d",&b[i]);lc[++top]=b[i];}
    	for(int i=1;i<=n;i++){scanf("%d",&a[i]);lc[++top]=a[i];}
    	sort(lc+1,lc+top+1);
    	for(int i=1;i<=top;i++)if(lc[i]!=lc[i-1])mp[lc[i]]=++cnt;
    	for(int i=1;i<=n;i++)
    	{
    		lc[mp[a[i]]]=a[i];
    		lc[mp[b[i]]]=b[i];
    		a[i]=mp[a[i]];
    		b[i]=mp[b[i]];
    		add(1,1,cnt,a[i],1,1);
    		add(1,1,cnt,b[i],0,1);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		int res=trns[1];
    		int l=get(1,1,cnt,b[i])+1;
    		int r=trrk[1];
    		add(1,1,cnt,b[i],0,-1);
    		if(l<=r)
    		{
    			while(l<r)
    			{
    				int mid=(l+r+1)>>1;
    				int tmp=ask(1,1,cnt,mid);
    				add(1,1,cnt,tmp,1,-1);
    				if(trns[1]+1==res)l=mid;
    				else r=mid-1;
    				add(1,1,cnt,tmp,1,1);
    			}
    			int tmp=ask(1,1,cnt,l);
    			add(1,1,cnt,tmp,1,-1);
    			if(trns[1]+1==res)
    			{
    				printf("%d ",lc[tmp]);
    				continue;
    			}
    			add(1,1,cnt,tmp,1,1);
    		}
    		l=1,r=get(1,1,cnt,b[i]);
    		while(l<r)
    		{
    			int mid=(l+r+1)>>1;
    			int tmp=ask(1,1,cnt,mid);
    			add(1,1,cnt,tmp,1,-1);
    			if(trns[1]==res)l=mid;
    			else r=mid-1;
    			add(1,1,cnt,tmp,1,1);
    		}
    		int tmp=ask(1,1,cnt,l);
    		add(1,1,cnt,tmp,1,-1);
    		printf("%d ",lc[tmp]);
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    基于V8引擎的C++和JS的相互交互
    C++和JavaScript脚本的相互调用
    用VC++MFC做文本编辑器(单文档模式)
    模式识别原理(Pattern Recognition)、概念、系统、特征选择和特征
    人工智能代码搜索
    QQ聊天机器人 Delphi代码
    代码自我清除 自我加密、解密的实现
    深入理解JVM虚拟机7:JNDI,OSGI,Tomcat类加载器实现
    深入理解JVM虚拟机6:深入理解JVM类加载机制
    深入理解JVM虚拟机5:虚拟机字节码执行引擎
  • 原文地址:https://www.cnblogs.com/wzc521/p/11813260.html
Copyright © 2011-2022 走看看