C
思路
我们发现对于max直接暴力即可 可是对于最小值就没有办法了 我们可以二分党派获得的票数 可是,由于有5%的限制,直接二分不可行 此时我们可以思考一下终态:
一定会有一定数量
( c )
的党派有一定
( x )
数量席位 想到这里,我们可以通过枚举
c
再二分
x
#include<bits/stdc++.h>
using namespace std ;
const int M=105 ;
struct node{
int x,s,i;
bool operator <(const node&A)const {
if (x*A.s!=A.x*s)return x*A.s<A.x*s;
return i>A.i;
}
}A[M],B[M],C[M];
int n,m,V,res,mi[M],t,Q[M],limt;
void MAX(){
for (int x=1 ;x<=n;x++){
for (int i=1 ;i<=n;i++)B[i]=A[i];
B[x].x+=res;
for (int c=1 ;c<=m;c++){
int k=0 ;
for (int i=1 ;i<=n;i++){
if (B[i].x<limt)continue ;
if (B[k]<B[i]||(!k))k=i;
}B[k].s++;
}
printf ("%d " ,B[x].s-1 );
}puts ("" );
}
bool check(int h,int x,int RRR){
for (int i=1 ;i<=n;i++)C[i]=B[i];
C[x].s=h+1 ;
int cas=m-h;
while (cas--){
int f=0 ,T=0 ,ned=0 ;
for (int i=1 ,y;i<=t;i++){
y=Q[i];
if (C[x]<C[y]){f=1 ;C[y].s++;break ;}
if (C[x].i<C[y].i){
int use=(C[x].x*C[y].s)/C[x].s-C[y].x+1 ;
if ((!T)||ned>use)T=y,ned=use;
}else {
int use=(C[x].x*C[y].s+C[x].s-1 )/C[x].s-C[y].x;
if ((!T)||ned>use)T=y,ned=use;
}
}
if (!f)RRR-=ned,C[T].s++,C[T].x+=ned;
if (RRR<0 )return 0 ;
}return 1 ;
}
void solve(int x){
if (A[x].x<limt)return ;
int &ans=mi[A[x].i];
ans=m;
for (int c=1 ;c<=n;c++){
for (int i=1 ;i<=n;i++)B[i]=A[i];
int Res=res;t=0 ;
for (int i=n;i>=1 &&t<=c;i--)if (i!=x){
Q[++t]=i;
if (B[i].x<limt)Res-=limt-B[i].x,B[i].x=limt;
}
if (Res<0 )continue ;
int L=0 ,R=ans;
while (L<=R){
int mid=L+R>>1 ;
if (check(mid,x,Res))ans=mid,R=mid-1 ;
else L=mid+1 ;
}
}
}
void MIN(){
sort(A+1 ,A+1 +n);
for (int i=1 ;i<=n;i++)solve(i);
for (int i=1 ;i<=n;i++)printf ("%d " ,mi[i]);
}
int main(){
scanf ("%d %d %d" ,&V,&n,&m);
res=V;limt=(V+19 )/20 ;
for (int i=1 ,x;i<=n;i++){
scanf ("%d" ,&x);
res-=x;
A[i]=(node){x,1 ,i};
}
MAX(),MIN();
return 0 ;
}
小结
对于该类问题,我们可以通过分析终态进行分析,不要拘泥于答案本身,需要挖掘最终答案的形成形式 如果对于一些限制条件(也可以是二分)不知道怎么判断是否可行的时候,可以分析权值是怎么变化的以及它的变化因素是什么,从而更加清晰的分析问题。
例如该题,我们发现直接判断
x
党派投k 票很困难,我们就可以逆向的考虑,即如何把除了
k
票以外的票都给其他党派。于是我们直接把k 票都先给
x
党(使他的权值最不优)。
D
思路
很容易发现直接枚举x 是不可能的 此时我们要尝试去枚举一些容易枚举的东西
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=a;i<=b;++i)
using namespace std ;
typedef long long ll;
const int M=20 ;
int pri[]={2 ,3 ,5 ,7 },a[4 ];
int fac[10 ][4 ]={
{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,0 ,0 ,0 },{0 ,1 ,0 ,0 },{2 ,0 ,0 ,0 },
{0 ,0 ,1 ,0 },{1 ,1 ,0 ,0 },{0 ,0 ,0 ,1 },{3 ,0 ,0 ,0 },{0 ,2 ,0 ,0 }
};
ll A,B,L,R,Dec[M],dp[M][30 ][19 ][13 ][11 ];
ll DP(int c,ll num){
ll mx=num+Dec[18 -c]-1 ;
if (mx<L||num>R)return 0 ;
if (c==18 )return !a[0 ]&&!a[1 ]&&!a[2 ]&&!a[3 ];
bool hav=num>=L&&mx<=R;
ll res=0 ,&ans=dp[c][a[0 ]][a[1 ]][a[2 ]][a[3 ]];
if (hav&&~ans)return ans;
for (int i=0 <num;i<10 ;++i){
bool f=1 ;
For(j,0 ,3 )if (a[j]<fac[i][j]){f=0 ;break ;}
if (!f)continue ;
For(j,0 ,3 )a[j]-=fac[i][j];
res+=DP(c+1 ,i*Dec[17 -c]+num);
For(j,0 ,3 )a[j]+=fac[i][j];
}
if (hav)ans=res;
return res;
}
ll pre(int c,ll pro){
if (pro*pro<0 ||pro*pro>B)return 0 ;
if (c==4 ){
L=(A+pro-1 )/pro;R=B/pro;
return DP(0 ,0 );
}
ll res=pre(c+1 ,pro);
a[c]++;
res+=pre(c,pro*pri[c]);
a[c]--;
return res;
}
int main(){
scanf ("%lld%lld" ,&A,&B);
Dec[0 ]=1 ;
For(i,1 ,M-1 )Dec[i]=Dec[i-1 ]*10 ;
memset (dp,-1 ,sizeof (dp));
printf ("%lld\n" ,pre(0 ,1 ));
return 0 ;
}
小结
枚举信息时要明确枚举的信息再接上其他的算法 量化枚举信息可以做到事半功倍的效果