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;
}
小结
- 枚举信息时要明确枚举的信息再接上其他的算法
- 量化枚举信息可以做到事半功倍的效果