20170703练习赛比赛总结

来源:互联网 发布:js代码整理 编辑:程序博客网 时间:2024/06/05 10:38

C

思路

  • 我们发现对于max直接暴力即可
  • 可是对于最小值就没有办法了
  • 我们可以二分党派获得的票数
  • 可是,由于有5%的限制,直接二分不可行
  • 此时我们可以思考一下终态:
    • 一定会有一定数量(c)的党派有一定(x)数量席位
  • 想到这里,我们可以通过枚举c再二分x
/*    思路:对于最大值,我们只需要把多的票都给它即可                       对于最小值,我们可以先枚举有多少个党派可以有席位之后二分最小席位即可           在二分时,我们直接先把x强行填成mid票          之后再每一次选举时,如果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){    /*init*/     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;}            /*找cost小的人了*/             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++){        /*init*/        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;/*4个小时!!!!!!*/        }        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是不可能的
  • 此时我们要尝试去枚举一些容易枚举的东西
/*    我们发现每个F(x)都可以表示成x*2^a[0]*3^a[1]*5^a[2]*7^a[3]    因为a[]的组合并不多我们可以通过枚举a[]来计算    此时我们可以套入记忆化搜索来实现加速   */#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]];    /*如果L<=[mi,mx]<=R 即随便放都可以 于是我们用记忆化搜索*/     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];    }    /*否则不更新DP的答案*/     if(hav)ans=res;    return res;}ll pre(int c,ll pro){    /*pro*pro可能炸掉然后小于0了*/     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;}

小结

  • 枚举信息时要明确枚举的信息再接上其他的算法
  • 量化枚举信息可以做到事半功倍的效果