https://codeforces.com/gym/102082
题意
两个人玩n轮游戏,每次两个人各取一个数,并且已知对手的取数顺序,如果你取的数比他大就算赢一轮,求能赢最多轮次的取法,如果有多种取法输出字典序最大的取法
题解
如果不规定字典序最大随意输出一种那我们可以排下序搞一搞就行了,但现在要保证字典序最大,怎么办呢?有经验的选手就知道,像保证什么最小的情况下求最大以及什么最大的情况求最小这种套路很有可能是二分。对本题来说,我们现在就是要保证当前取法满足条件的情况下字典序最大,我们贪心取当前数最大看后面是否满足,而且数越大后面越不容易满足,换句话说这种取数其实是单调的,所以我们可以二分来做。首先我们要先求出最大可以赢的轮次ans,然后从前往后扫一遍,看当前最多可以拿到多大的bi满足赢的轮次为ans。时间复杂度n^2。
1 #define IO std::ios::sync_with_stdio(0); 2 #include <bits/stdc++.h> 3 #define iter ::iterator 4 using namespace std; 5 typedef long long ll; 6 typedef pair<ll,ll>P; 7 #define pb push_back 8 #define se second 9 #define fi first 10 #define rs o*2+1 11 #define ls o*2 12 const int N=5e3+5; 13 vector<int>a,b,tmp; 14 int ans,n; 15 int check(int start,int v,vector<int>s){ 16 int res=(v>a[start]); 17 int flag=0; 18 int p=s.size()-1; 19 for(int i=start+1;i<n;i++){ 20 if(s[p]==v&&!flag){ 21 flag=1; 22 p--; 23 } 24 if(s[p]>a[i]){ 25 p--; 26 res++; 27 } 28 } 29 return res==ans; 30 } 31 int main(){ 32 scanf("%d",&n); 33 a.resize(n); 34 b.resize(n); 35 tmp.resize(n); 36 for(int i=0;i<n;i++){ 37 scanf("%d",&a[i]); 38 tmp[i]=a[i]; 39 } 40 for(int i=0;i<n;i++){ 41 scanf("%d",&b[i]); 42 } 43 sort(b.begin(),b.end()); 44 sort(a.begin(),a.end(),greater<int>()); 45 multiset<int>s; 46 for(int i=0;i<n;i++){ 47 s.insert(b[i]); 48 } 49 multiset<int>iter it; 50 for(int i=0;i<n;i++){ 51 it=--s.end(); 52 if(*it>a[i])ans++; 53 else it=s.begin(); 54 s.erase(it); 55 } 56 for(int i=0;i<n;i++){ 57 copy(tmp.begin(),tmp.end(),a.begin()); 58 sort(a.begin()+i+1,a.end(),greater<int>()); 59 int l=upper_bound(b.begin(),b.end(),a[i])-b.begin()-1; 60 int r=b.size()-1; 61 while(l<r){ 62 int m=l+(r-l+1)/2; 63 if(check(i,b[m],b))l=m; 64 else r=m-1; 65 } 66 if(check(i,b[r],b)){ 67 if(b[r]>a[i])ans--; 68 printf("%d ",b[r]); 69 b.erase(b.begin()+r); 70 } 71 else{ 72 int l=0,r=upper_bound(b.begin(),b.end(),a[i])-b.begin()-1; 73 while(l<r){ 74 int m=l+(r-l+1)/2; 75 if(check(i,b[m],b))l=m; 76 else r=m-1; 77 } 78 printf("%d ",b[r]); 79 b.erase(b.begin()+r); 80 } 81 } 82 printf(" "); 83 }