zoukankan      html  css  js  c++  java
  • [atARC112F]Die Siedler

    1和2操作是独立的,换言之一定可以先执行1操作选择包裹,再执行2操作使得$0le c_{i}<2i$

    对于$c_{i}$,将其看作一个进制转换,并以$c_{i}$为从低到高的第$i$位,系数即为$2^{i-1}(i-1)!$

    将其乘权累加,即令$c_{0}=sum_{i=1}^{n}2^{i-1}(i-1)!c_{i}$,考虑2操作对$c_{0}$的影响:

    1.对于$i$($1le i<n$)的操作,显然不会改变$c_{0}$

    2.对于$n$的操作,即将$c_{0}$减去$2^{n}n!-1$(以下记作$N$)

    根据最终$0le c_{i}<2i$,代入即得到最终的$c'_{0}$满足
    $$
    c'_{0}le sum_{i=1}^{n}(2i-1)2^{i-1}(i-1)!=sum_{i=1}^{n}2^{i}i!-sum_{i=1}^{n}2^{i-1}(i-1)!=N
    $$
    1.对于$c_{0} otequiv 0(mod N)$的情况,$c'_{0}$一定是$c_{0}$对$N$取模的结果

    2.对于$c_{0}equiv 0(mod N)$的特殊情况下,则$c'_{0}=0或N$,由于初始以及任意一次操作后都会有$sum_{i=1}^{n}c_{i}>0$,即$c'_{0} e 0$,因此$c'_{0}=N$

    进一步的,确定最终的$1le c'_{0}le N$后,根据$0le c_{i}<2i$,我们也可以从高到低依次确定$c_{i}$,那么也就是说我们仅关心于最终能产生哪些$c'_{0}$

    更具体的,令$c_{0}=sum_{i=1}^{n}2^{i-1}(i-1)!c_{i}$,$s_{i}=sum_{j=1}^{n}2^{j-1}(j-1)!s_{i,j}$,观察式子可以发现每一项对应相加即为整体相加,因此取第$i$个包裹即令$c_{0}+=s_{i}$(模$N$意义下相加)

    每一次都是加上$s_{i}$或减去$N$(模$N$),根据扩欧,有$c'_{0}equiv c_{0}(mod gcd(N,s_{1},s_{2},...,s_{n}))$,同时可以构造出一组整数解(不保证$s_{i}$系数非负且$N$系数非正),通过这组解,不断令$s_{i}$的系数加上$N$、$N$的系数减去$s_{i}$使其满足上面的两个条件,因此都是可以的

    令$d=gcd(N,s_{1},s_{2},...,s_{n})$,来考虑两种做法:

    1.暴力枚举所有$c'_{0}$(要求$1le c'_{0}le N$)并构造出对应$c_{i}$,然后求出答案,这一做法复杂度为$o(frac{nN}{d})$

    2.先来构造最终的$c_{i}$,即要使得$sum_{i=1}^{n}2^{i-1}(i-1)!c_{i}equiv c_{0}(mod d)$

    这个可以看作从$c_{i}={0,0,...,0}$开始,将其中某一个$c_{i}$加1,移动到另一个点,由于我们仅关心于其模$d$的余数,因此可以看作一个大小为$d$的图,bfs即可,时间复杂度为$o(nd)$

    (当$c_{0}equiv 0(mod d)$时需要特判,因为不能令$c_{i}={0,0,...,0}$)

    由此,我们得到了一个$o(nsqrt{N})$的做法,但复杂度似乎还不能接受

    注意到$N=2^{n}n!-1$,$d$必然是其因子,而由于$N$形式的特殊性,打表可得
    $$
    max_{2le nle 16,N=2^{n}n!-1,d|N}nmin(frac{N}{d},d)le 14577924
    $$
    (最大值是在$n=12$时取到,$d=1214827$)

    上面这个式子即复杂度,那么就可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define oo 0x3f3f3f3f
     5 queue<int>q;
     6 vector<int>dis;
     7 int n,m,x,ans;
     8 ll N,d,c,val[21];
     9 ll gcd(ll x,ll y){
    10     if (!y)return x;
    11     return gcd(y,x%y);
    12 }
    13 int main(){
    14     scanf("%d%d",&n,&m);
    15     val[1]=1;
    16     for(int i=2;i<=n;i++)val[i]=2*(i-1)*val[i-1];
    17     d=N=2*n*val[n]-1;
    18     for(int i=1;i<=n;i++){
    19         scanf("%d",&x);
    20         c+=x*val[i];
    21         ans+=x;
    22     }
    23     for(int i=1;i<=m;i++){
    24         ll s=0;
    25         for(int j=1;j<=n;j++){
    26             scanf("%d",&x);
    27             s+=x*val[j];
    28         }
    29         d=gcd(d,s);
    30     }
    31     if (d>N/d){
    32         for(ll i=c%d;i<=N;i+=d){
    33             if (!i)continue;
    34             ll k=i;
    35             int s=0;
    36             for(int j=n;j;j--){
    37                 s+=k/val[j];
    38                 k%=val[j];
    39             }
    40             ans=min(ans,s);
    41         }
    42         printf("%d",ans);
    43         return 0;
    44     }
    45     for(int i=0;i<d;i++)dis.push_back(oo);
    46     dis[0]=0;
    47     q.push(0);
    48     while (!q.empty()){
    49         int k=q.front();
    50         q.pop();
    51         for(int i=1;i<=n;i++)
    52             if (dis[(k+val[i])%d]==oo){
    53                 dis[(k+val[i])%d]=dis[k]+1;
    54                 q.push((k+val[i])%d);
    55             }
    56     }
    57     if (c%d)printf("%d",dis[c%d]);
    58     else{
    59         for(int i=1;i<=n;i++)ans=min(ans,dis[(N-val[i])%d]+1);
    60         printf("%d",ans);
    61     }
    62 }
    View Code
  • 相关阅读:
    康拓展开和康拓逆展开
    快速乘法(基于快速幂)
    扩展欧几里德 POJ 1061
    九度OJ 1552座位问题(dp)
    UVA-10462.Is There A Second Way Left(Kruskal+次小生成树)
    POJ-1679.The Unique MST.(Prim求次小生成树)
    次小生成树(Prim + Kruaskal)
    POJ-1287.Network(Kruskal + Prim + Prim堆优化)
    最小生成树基础算法(Prim + Krustal)
    POJ-2492.A Bug's Life(带权并查集)
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14435881.html
Copyright © 2011-2022 走看看