题意:
——默认你已仔细阅读过题干,并且了解欧拉函数。
简化版题意:
——题目已给出 m 的质因数分解式,找 m 的所有约数,约数可分为3类(政客,军人,学者),每个约数只能代表三者其一。即:所以政客+所有军人+所有学者==m的所有约数。
注意:所有约数中不包括1,因为 1 不属于三者中任意一个。
——所求:所有政客的独立数之和,所有军人的独立数之和,所有学者的独立数之和。
质因数分解式就是找约数使用的。
注释:
独立数:
x 的独立数:就是 x 的欧拉函数值。
政客:
能分解成偶数个不同奇质数的乘积.(质因数的指数小于等于1)
军人:
能分解成奇数个不同奇质数的乘积.(质因数的指数小于等于1)
学者:
不满足1、2的约数.(约数1除外,并且该约数较多)
如何解题:
计算 m 所有约数独立数之和ans
计算 m 所有政客独立数之和ans1
计算 m 所有军人独立数之和ans2
所有学者独立数之和=ans-ans1-ans2。
——如何计算 m 所有约数独立数之和?(包括1)
答:该值就是 m 。复杂的证明方式(不建议看),点击它,找到题解的第二行。
——计算ans1和ans2。
计算ans1和ans2不考虑指数,且质数2也不用考虑,因为政客和军人的定义。
想象一下:面前摆放着 n 个不同的质数。
挑奇数个质数就表示政客
挑偶数个质数就表示军人
每个质数都有2种选择:选或不选。【dfs或者dp。该题个数较多,只能dp】
奇偶个数相差1,因此二者之间计算有关联,需要用到欧拉函数的积性性质。
计算需要用到欧拉函数性质:
——欧拉函数是积性函数,如果 i,j 互质,那么phi ( i * j ) = phi ( i ) * phi ( j )。
如何得到所有约数的欧拉函数值累加和:dp思想
——用f [ i ] [ j ] 表示选到第 i 个质数一共选了 j 个质数的 phi 的累加和。
比如说:
f [ 5 ] [ 1 ] 表示当选到第5个质数时,只选 1 个质数,
其实我们有5中选择,
即 f [ 5 ] [ 1 ] = f [ 1 ] [ 1 ] + f [ 2 ] [ 1 ] + f [ 3 ] [ 1 ] + f [ 4 ] [ 1 ] + f [ 5 ] [ 1 ] ,
这里可一用到累加和思想,
即 f [ 5 ] [ 1 ] = f [ 4 ] [ 1 ] + f [ 5 ] [ 1 ]
注意:
——后加的 f [ 5 ] [ 1 ] 仍然是未知的,怎么得到,其实它是从前一个状态 f [ 4 ] [ 0 ] 再选一个 p [ 5 ] 得到的,这里牵涉边界0了,推的式子不是通式。
通式版:
——如何得到 f [ i ] [ j ](此刻仍不是累加和),从前一个状态f [ i - 1 ] [ j - 1 ] 再选一个 p [ i ]就可得到 f [ i ] [ j ]。
根据积性函数 f [ i ] [ j ] = f [ i - 1 ] [ j - 1 ] * ( p [ i ] - 1 )。
所以状态转移方程为:
当 j ! = 1 时
f [ i ] [ j ] = f [ i - 1 ] [ j ] + f [ i - 1 ] [ j - 1 ] * ( p [ i ] - 1 )
当 j == 1 时
f [ i ] [ j ] = ( f [ i - 1 ] [ 1 ] + p [ i ] - 1)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
const ll MOD=10000;
ll p[N],e[N],f[N][N];
ll quickMod(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans*x%MOD;
x=x*x%MOD;
y>>=1;
}
return ans%MOD;//未进入循环&&MOD=1
}
int main()
{
ll n,i,j;
scanf("%lld",&n);
for(i=1;i<=n;i++)
scanf("%lld %lld",&p[i],&e[i]);
ll ans=1;
for(i=1;i<=n;i++)
ans=ans*quickMod(p[i],e[i])%MOD;
if(p[1]==2)
{
for(i=1;i+1<=n;i++)
p[i]=p[i+1];
n--;
}
memset(f,0,sizeof(f));
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
if(j==1) f[i][1]=(f[i-1][1]+p[i]-1)%MOD;
else f[i][j]=(f[i-1][j]+f[i-1][j-1]*(p[i]-1)%MOD)%MOD;
ll ans1=0,ans2=0,ans3;
for(i=2;i<=n;i+=2) ans1+=f[n][i],ans1%=MOD;
for(i=1;i<=n;i+=2) ans2+=f[n][i],ans2%=MOD;
ans3=((ans-1-ans1-ans2)%MOD+MOD)%MOD;
printf("%lld\n%lld\n%lld\n",ans1,ans2,ans3);
return 0;
}
下面的dp不对。
memset(f,0,sizeof(f));
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
if(j==1) f[i][j]=(p[i]-1)%MOD;
else f[i][j]=(f[i-1][j-1]*(p[i]-1)%MOD)%MOD;
//后累加不行
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
f[i][j]=(f[i-1][j]+f[i][j])%MOD;
这里的f[i][j]不表示累加和,只表示面对第i个时,选j个约数的的欧拉值,突然想起来确实不对,从i里面选j个,本来就有多种选项,不使用累加和,会影响之后的运算,因为 f [ i ][ j ]只有一次被计算的机会。