【NOI2014模拟】疫苗(burnside引理)

Description:

中二病正在危害人类!不知何时爆发的中二病,起初还毫无任何症状,最近却连续出现致命症状,已经成为了一种不治之症。科学家们正在着手研发疫苗。

科学家从患者体内提取出了中二病病原体,并找出了其致病DNA。这条DNA可以被分为n段,第n段和第1段相接,呈环形。要制作疫苗的话,需要从中取出若干段DNA。

然而,中二病的生存和复制能力特别强。如果疫苗中存在两段在原DNA中距离小于k的DNA,这两段就可以复制出整条DNA。因此,科学家选择的若干段DNA彼此之间的距离都应该不小于k。

这里定义距离为两段DNA在原序列中间隔的最少段数+1,如相邻的两段距离为1。自己与自己的距离不做定义,换言之,不论的值是多少,总能选出一段DNA。

为了使疫苗对于各种属性的人都有效,科学家想制作尽量多种的疫苗。也就是说,要在满足上面的条件的情况下,选出尽量多组不同的DNA段,分别制成疫苗。当然,选出这样若干段的方案数有很多,但是有些方案在旋转之后是相同的,因此在本质上也是相同的。

请你编写一个程序,求出最多能制作的疫苗种数,以及最多能制作的本质不同的疫苗种数。由于答案可能很大,输出答案对10^9+7取模得到的结果。

题解:

首先要搞出第一问的dp。

fi 表示n=i,k就是那个k的方案数。

显然有
fi=i(i<2k)
fi=fi1+fik+1(i>=2k)

第二种转移就是考虑在i放不放东西。

不放就是 fi1

放的话考虑 fik 的一种方案,
其开头和结尾的差距一定不小于k,但是我如果在i这里放,和 fik 的方案的开头的差距可能小于k,此时把 fik 方案移动一定距离就保证和 fik 的方案的开头的差距可能大于等于k,倒推一下可以证明这样可以表示出任意一种情况。

由于 fik 少了不选的那种情况,所以+1。

第二问就是burnside引理:
=

在这题,置换就是平移n次。

枚举平移的长度i,

相当于求 nj=1[ai=ai+j]

设平移y次能够回到原点。

iy=0(mod n)
y=0(mod ngcd(i,n))

因此循环节的长度是 gcd(i,n)

所以 Ans=ni=1f(gcd(i,n))n

Code:

#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int mo = 1e9 + 7;

const int N = 2e5 + 5;

int n, k;
ll a[N], ans;

int gcd(int x, int y)  {return !y ? x : gcd(y, x % y);}

ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y /= 2, x = x * x % mo)
        if(y & 1) s = s * x % mo;
    return s;
}

int main() {
    scanf("%d %d", &n, &k);
    a[k] = k;
    fo(i, k + 1, 2 * k - 1) a[i] = a[i - 1] + 1;
    fo(i, 2 * k, n) a[i] = (a[i - 1] + a[i - k] + 1) % mo;
    printf("%lld\n", a[n]);
    fo(i, 1, n) ans = (ans + a[gcd(i, n)]) % mo;
    ans = ans * ksm(n, mo - 2) % mo;
    printf("%lld", ans);
}

你可能感兴趣的:(动态规划,数论杂集)