JZOJ 5796. 2018.08.10【2018提高组】模拟A组&省选 划分

 

有一个未知的序列x,长度为n。

它的K-划分序列y指的是每连续K个数的和得到划分序列,y[1]=x[1]+x[2]+....+x[K],y[2]=x[K+1]+x[K+2]+....+x[K+K]....。

若n不被K整除,则y[n/K+1]可以由少于K个数加起来。

比如n=13,K=5,则y[1]=x[1]+...+x[5],y[2]=x[6]+....+x[10],y[3]=x[11]+x[12]+x[13]。

若小A只确定x的K[1]划分序列以及K[2]划分序列....K[M]划分序列的值情况下,问她可以确定x多少个元素的值。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 5e6 + 10;
 4 typedef long long ll;
 5 //#define ll __int128
 6 int n, m, k[20], vis[N];
 7 
 8 ll x, y, ans;
 9 
10 void exgcd(ll a, ll b, ll &x, ll &y) {
11     if(!b) x = 1, y = 0;
12     else exgcd(b, a % b, y, x), y -= x * (a / b);
13 }
14 
15 ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
16 
17 ll getlcm(ll a, ll b) {
18     if(!a || !b) return a | b;
19     return a / gcd(a, b) * b;
20 }
21 
22 int main() {
23     freopen("sazetak.in", "r", stdin);
24     freopen("sazetak.out", "w", stdout);
25     scanf("%d%d", &n, &m);
26     for(int i = 1 ; i <= m ; ++ i) scanf("%d", &k[i]);
27     int lim = pow(3, m);
28     for(int s = 0 ; s < lim ; ++ s) {
29         ll lcm1 = 0, lcm2 = 0, c1 = 0, c2 = 0, flag = 0;
30         for(int i = 1, ss = s ; i <= m ; ++ i, ss /= 3) {
31             if(ss % 3 == 1) lcm1 = getlcm(lcm1, k[i]), ++ c1;
32             else if(ss % 3 == 2) lcm2 = getlcm(lcm2, k[i]), ++ c2;
33             if(lcm1 > n || lcm2 > n) { flag = 1; break; }
34         }
35         if(gcd(lcm1, lcm2) != 1 || !c1 || !c2 || flag) continue;
36         exgcd(lcm1, lcm2, x, y);
37         ll lcm = lcm1 * lcm2;
38         x = (x % lcm2 + lcm2) % lcm2;
39         x = (x * lcm1 % lcm + lcm) % lcm;
40         if(x + 1 >= n) continue;
41         if((c1 + c2) & 1) ans -= (n - x - 1) / lcm + 1;
42         else ans += (n - x - 1) / lcm + 1;
43     }
44     for(int i = 1 ; i <= m ; ++ i) {
45         if(n % k[i] == 1) {
46             ++ ans;
47             break;
48         }
49     }
50     printf("%lld\n", (long long) ans);
51 }
JZOJ 5796. 2018.08.10【2018提高组】模拟A组&省选 划分

需要先知道一点,假如 x[p]能够求出来,那么必定存 在一对(i,j),满足 P mod k[i]=0,p mod k[j]=1

相当于 p=a1*k[i]=a2*k[j]+1 即 a1*k[i]-a2*k[j]=1,这个就可以用扩展欧几里得算法来求通解,

并且我们可以知道,有解的条件必定是 gcd(k[i],k[j])=1. 但是,这样做会重复计算。

我们现在就要去重。 我们可以设 f[s1][s2](其中 s1 表示 p mod k[i]=0 的集合, s2 表示 p mod k[j]=1 的集合,s1,s2 可以用二进制来压),

来表示满足以下方程的 p 的个数: P mod k[i1]=0, P mod k[i2]=0, P mod k[i3]=0, …… P mod k[j1]=1, P mod k[j2]=1, P mod k[j3]=1, ……

事实上,等价于满足两个方程。 P mod lcm(k[i1],k[i2],…)=0,P mod lcm(k[j1],k[j2],…)=1 这个方程的解的个数可以用扩展欧几里得来求。

然后存在 f[s1][s2]里。 接着我们只需要把 f[s1][s2]减去 f[s’][s’’],其中满足 s1∈s’且 s2∈s’’ ,这就相当于把重复的去掉。

时间复杂度是 O(m*3^m)

以上是题解

转载于:https://www.cnblogs.com/KingSann/articles/9457354.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值