[bzoj1361][Wc2004]孪生项链【dp】【字符串】【容斥原理】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=1361
【题解】
  先考虑第二问,显然是一个简单的容斥原理,记不循环同构的串的数量为 fi f i ,那么有:

fi=2ij|ifj f i = 2 i − ∑ j | i f j

  那么答案就是 fk/k f k / k
  记得要用高精度。
  第一问:我们可以先把给定的串倍长到 n n ,然后在末位+1,进位,并去掉末尾的0,这样子一定得到比它大的最小串。
  现在我们来证明这是对的,首先最小是肯定的,那么接下来就是是否循环同构了。考虑反证法,如果循环同构,那么后面的那一段在之前一定比前面的小,所以之前的串并不是最小的字典序表示,与给定的事实相反。
  时间复杂度O(K2+N)
【代码】

/* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   [bzoj1361]
    Points :    
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    M       200010
# define    N       1010
using namespace std;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}
struct INT{
    int len, num[N];
    void print(){
        for (int i = len; i > 0; i--)
            printf("%d", num[i]);
        printf("\n");
    }
}tnp, f[N], nxt;
INT operator -(INT &a, INT &b){
    nxt = a; int i;
    for (i = 1; i <= nxt.len; i++) nxt.num[i] -= b.num[i];
    for (i = 1; i < nxt.len; i++)
        if (nxt.num[i] < 0)
            nxt.num[i] += 10, nxt.num[i + 1] -= 1;
    while (nxt.num[nxt.len] == 0)
        nxt.len--;
    return nxt;
}
INT operator *(INT &a, int b){
    nxt = a; int i;
    for (i = 1; i <= nxt.len; i++) nxt.num[i] *= b;
    for (i = 1; i <= nxt.len || nxt.num[i] != 0; i++)
        nxt.num[i + 1] += nxt.num[i] / 10, nxt.num[i] %= 10;
    nxt.len = i - 1;
    return nxt;
}
INT operator /(INT &a, int b){
    memset(nxt.num, 0, sizeof(nxt.num));
    nxt.len = 0; int i, tmp = 0;
    for (i = a.len; i >= 1; i--){
        tmp = tmp * 10 + a.num[i];
        nxt.num[i] = tmp / b;
        nxt.len = max(nxt.len, (nxt.num[i] > 0) ? i : 0);
        tmp %= b;
    }
    return nxt;
}
char mp[M];
int n, m, k;
int main(){
//  freopen("bzoj1361.in", "r", stdin);
//  freopen("bzoj1361.out", "w", stdout);
    n = read(), m = read(), k = read();
    scanf("\n%s", mp + 1);
    tnp.len = 1, tnp.num[1] = 1;
    for (int i = 1; i <= k; i++){
        tnp = tnp * 2;
        f[i] = tnp;
        for (int j = 1; j < i; j++)
            if (i % j == 0) f[i] = f[i] - f[j];
    }
    f[k] = f[k] / k; 
    f[k].print();
    for (int i = 1; i <= m; i++) mp[i] -= '0';
    for (int i = m + 1; i <= n; i++)
        mp[i] = mp[(i - 1) % m + 1];
    mp[n]++;
    int tmp = n;
    while (mp[tmp] == 2){
        mp[tmp--] = 0;
        mp[tmp]++;
    }
    for (int i = 1; i <= tmp; i++)
        printf("%d", mp[i]);
    printf("\n");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值