汤姆波利伯~题目在这里。
一、三分
关键是找到x轴和y轴的表示意义,x轴表示的是最终得票数,y轴是花费 --- 联系题意和要求,题目给我们的就是投给谁和花费,那我们的已知也只有这个,要找的就是最好的一种组合,既能保证赢,花费又最小,而这个组合减或增都不满足这两个要求最优,不是单调的,所以可以想到是一个开口向上的抛物线,所求解即是抛物线最低点 --- 即我们要找一个票数,使花费最小又能赢。
所以三分时,就不断地找她的最低点,但注意要留有余量,最后特判就好。
need表示的是离目标票数还差几票,所以我们最终是为了得到这个票数。之后的循环是为了找到比这个目标票数还高的人,那我们就一定要买过来,同时need自减,这个过程need是否达标不是主要,主要是为让别人不超目标票数,所以就算need变负了也没关系。要是都遍历后need还大于0,那我们就从剩下没拿的之中找最小的加上去就行。这里面的操作用vector就很方便了。
1 #include<bits/stdc++.h> 2 #include<algorithm> 3 #include<iostream> 4 #define mem(a) memset(a,0,sizeof(a)) 5 #define ll long long 6 #define inf 0x3f3f3f3f 7 const int N=1e5+5; 8 const int mod=1e9+7; 9 using namespace std; 10 int p,q,y[N],tx,ty; 11 char s[N]; 12 int n,mx,a,b; 13 vector<int>ve[N],v; 14 ll third(int tag) 15 { 16 int ans=0; 17 int need=tag-ve[0].size(); //预设想要拿的票数减去现有票数,即还需几票 18 v.clear(); 19 for(int i=1;i<=mx;i++) //遍历每个人 20 { 21 for(int j=0;j<ve[i].size();j++) 22 { 23 if(ve[i].size()-j>=tag) //要把多数派做掉先,tag可能设的太小,所以其实可能会超 24 { 25 ans+=ve[i][j];//已经排过序了,所以从小到大放心拿 26 need--; 27 } 28 else 29 v.push_back(ve[i][j]); 30 } 31 } 32 sort(v.begin(),v.end()); 33 for(int i=0;i<need;i++) //如果还不够,排完序继续拿 34 ans+=v[i]; 35 return ans; 36 } 37 int main() 38 { 39 cin>>n; 40 for(int i=0;i<n;i++) 41 { 42 cin>>a>>b; 43 ve[a].push_back(b); 44 mx=max(mx,a); 45 } 46 for(int i=1;i<=mx;i++) 47 sort(ve[i].begin(),ve[i].end()); 48 int l=0,r=n; //不是每个人,而是票数 (。>︿<)_θ 49 while(l+2<r) //这里注意要留常数特判,因为可能lr因为下面等于的原因,会漏掉之类 50 { 51 // cout<<"***"<<l<<" "<<r<<endl; 52 int mid=(l+r)>>1,mmid=(mid+r)>>1; 53 if(third(mid)>third(mmid)) l=mid; 54 else r=mmid; 55 // cout<<"---"<<l<<" "<<r<<endl; 56 } 57 ll ans=inf; 58 for(int i=l;i<=r;i++) 59 ans=min(third(i),ans); 60 cout<<ans<<endl; 61 return 0; 62 }
二、线段树
//学习别人的,但是,还没完全看懂,继续mark
1 #include<bits/stdc++.h> 2 #include<iostream> 3 #include<stack> 4 #include<algorithm> 5 #include<cstdio> 6 #include<cmath> 7 #include<cstring> 8 #define mem(a) memset(a,0,sizeof(a)) 9 #define memm(a) memset(a,inf,sizeof(a)) 10 #define ll long long 11 #define ld long double 12 #define uL unsigned long long 13 #define mp make_pair 14 #define pb push_back 15 #define inf 0x3f3f3f3f 16 using namespace std; 17 const int N=1e5+5; 18 const int M=100+5; 19 int n,s,k,x[N],y[N],z[N]; 20 int cnt,sum,mx; 21 vector<int>ve[N],v[N]; 22 struct node 23 { 24 int l,r,sz,sum; 25 }tree[4*N+5]; 26 void push_up(int rt) 27 { 28 tree[rt].sz=tree[rt<<1].sz+tree[rt<<1|1].sz; 29 tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum; 30 } 31 void build(int rt,int l,int r) 32 { 33 tree[rt].l=l; 34 tree[rt].r=r; 35 tree[rt].sz=tree[rt].sum=0; 36 if(l==r) return; /// 37 int mid=(l+r)>>1; 38 build(rt<<1,l,mid); 39 build(rt<<1|1,mid+1,r); 40 41 } 42 void update(int rt,int num,int x) 43 { 44 if(tree[rt].l==tree[rt].r) 45 { 46 tree[rt].sz+=num; 47 tree[rt].sum+=x*num; 48 return; 49 } 50 int mid=(tree[rt].l+tree[rt].r)>>1; 51 if(x<=mid) 52 update(rt<<1,num,x); 53 else 54 update(rt<<1|1,num,x); 55 push_up(rt); 56 } 57 int query(int rt,int need) 58 { 59 if(need<=0) return 0; 60 if(tree[rt].sz<=need) return tree[rt].sum; 61 if(tree[rt].l==tree[rt].r) return need*tree[rt].l; /// 62 //int mid=(tree[rt].l+tree[rt].r)>>1; 63 if(tree[rt<<1].sz<=need) return tree[rt<<1].sum+query(rt<<1|1,need-tree[rt<<1].sz); 64 else return query(rt<<1,need); 65 } 66 int solve(int t) 67 { 68 for(int i=0;i<v[t].size();i++) 69 { 70 sum+=v[t][i]; 71 update(1,-1,v[t][i]); 72 cnt++; 73 } 74 int need=t-cnt; 75 if(need<=0) return sum; 76 return sum+query(1,need); 77 } 78 int main() 79 { 80 cin>>n; 81 int a,b; 82 build(1,0,N); //0开始 83 for(int i=1;i<=n;i++) 84 { 85 cin>>a>>b; 86 ve[a].pb(b); 87 mx=max(mx,a); 88 update(1,1,b); 89 } 90 for(int i=1;i<=mx;i++) 91 sort(ve[i].begin(),ve[i].end()); 92 for(int i=0;i<=mx;i++) 93 { 94 for(int j=0;j<ve[i].size();j++) 95 v[ve[i].size()-j].pb(ve[i][j]); /// 96 } 97 int ans=inf; 98 for(int i=n;i>=0;i--) ///预设票数,从大到小,找临界点 99 { 100 int temp=solve(i); 101 if(temp==-1) break; 102 ans=min(ans,temp); 103 } 104 cout<<ans<<endl; 105 return 0; 106 }