[atARC112F]Die Siedler

1和2操作是独立的,换言之一定可以先执行1操作选择包裹,再执行2操作使得$0\le 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$($1\le i<n$)的操作,显然不会改变$c_{0}$

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

根据最终$0\le 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}\not\equiv 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}\ne 0$,因此$c'_{0}=N$

进一步的,确定最终的$1\le c'_{0}\le N$后,根据$0\le 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}$(要求$1\le 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(n\sqrt{N})$的做法,但复杂度似乎还不能接受

注意到$N=2^{n}n!-1$,$d$必然是其因子,而由于$N$形式的特殊性,打表可得
$$
\max_{2\le n\le 16,N=2^{n}n!-1,d|N}n\min(\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

 

posted @ 2021-02-23 14:35  PYWBKTDA  阅读(148)  评论(0编辑  收藏  举报