您的位置:首页 > 其它

[数论]Irrelevant Elements, ACM/ICPC NEERC 2004, Uva1635

2017-12-27 21:51 399 查看
[唯一分解定理在组合数学中的简单应用]

题意:给定n个数a1,a2…an,依次求出相邻两个数之和,将得到一个新数列。重复这个操作,最终将得到一个数。问这个数除以m的余数与原数列中的哪个数无关。

题解:最后的求和显然是a1,a2…an的一个线性组合。设ai的系数为f(i),那么当且仅当f(i)是m的倍数时,最终结果除以m的余数与ai无关。

此时这个问题变成:找出最终的线性组合中哪些项的系数是m的倍数。

题目中给出的操作是不断将相邻的两个数相加,每一层的数都可以表示为上一层两个相邻数的和,显然这是一个杨辉三角,而它的性质就是第n行的第i个数为C(n-1,i-1)。

此时这个问题变成:对于给定的一个整数n,找出C(n-1,0)到C(n-1,n-1)这n个数中有哪些数是m的倍数。

理论上可以利用C(n,k)=C(n,k-1)*(n-k+1)/k递推出所有的组合数,但n的范围1e5,只能使用高精度。这时需要注意到,本题只是要判断哪些是m的倍数,并不要求求出所有的组合数。这里利用数论中的唯一分解定理,依次计算m的唯一分解式中各个素因子在C(n-1,i-1)中的指数即可完成判断,而对组合数进行判断时仍然可以使用上述公式进行递推。

还需注意的是,本题判断m的倍数不能采取递推取模的方法。原因是递推式中含有除法,而模m意义下的逆元只有在与m互质时才存在。

//80ms
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxp 32001
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n, m, im, ans, en;
int prime[maxp], pnum = 0, pm[20], em[20];
bool flag[maxp], vis[maxn];

void getpri()
{
memset(flag, 0, sizeof(flag));
int p = sqrt(maxp + 0.5);
for (int i = 2; i <= p; i++)
{
for (int j = i*i; j < maxp; j += i)
flag[j] = 1;
}
for (int i = 2; i < maxp; i++)
{
if (!flag[i])
prime[pnum++] = i;
}
}

void init()
{
memset(pm, 0, sizeof(pm));
memset(em, 0, sizeof(em));
im = 0;
for (int i = 0; i < pnum&&prime[i] <= m; i++)
{
if (m%prime[i] == 0)
{
pm[im] = prime[i];
while ((m%prime[i] == 0) && (m /= prime[i]))
em[im]++;
im++;
}
if (n == 0 || n == 1)
break;
}
if (m > 1)
{
pm[im] = m;
em[im++] = 1;
}
}

bool getfac(int x, int y)
{
bool cnt = true;
for (int i = 0; i < im; i++)
{
while (x%pm[i] == 0 && (x /= pm[i]))
em[i]--;
while (y%pm[i] == 0 && (y /= pm[i]))
em[i]++;
if (em[i] > 0)
cnt = false;
}
return cnt;
}

int main()
{
getpri();
while (scanf("%d%d", &n, &m) != EOF)
{
init();
ans = en = 0;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; ++i)
{
if (getfac(n - i, i))
{
vis[i + 1] = true;
ans++;
en = i + 1;
}
}
printf("%d\n", ans);
if (ans)
{
for (int i = 1; i < en; i++)
{
if (vis[i])
printf("%d ", i);
}
printf("%d", en);
}
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  uva 数学