【题目大意】

给出一个$n$个数的序列$\{a_n\}$,其中有些地方的数为0,要求你把这个序列填成一个1到$n$的排列,使得:

$(a_i, a_j) = 1$,当且仅当$(i, j) = 1$。多组数据。

$n \leq 3\times 10^5, T\leq 10$

CodeForces:无多组数据,$n \leq 10^6$

【题解】

这题有点神奇啊。。

首先考虑序列全是0要怎么做。

考虑到如果两个数的位置含有的因数种类完全一样,那么它们是可以互换的。(这个挺显然的)

观察如果两个质数的出现次数相同,那么这两个质数的位置是可以互换的。

第一个结论的解释是:比如位置2,位置4,原来它们对应关系是2-2,4-4,那么变成2-4,4-2也仍然合法。(显然其他也成立)

第二个结论的解释是:比如2和3的出现次数相同,就是意思说$\lfloor n/2 \rfloor = \lfloor n/3 \rfloor$,那么可以把含有2的因数的所有数(比如1*2, 2*2, 3*2, ...)和含有3因数的所有数(1*3, 2*3, 3*3, ...)对应互换。这个显然是没有问题的。

那么全0的话,就分别计算两个结论的贡献即可。

如果有很多数可以互相互换,那么设这些数的数量为$n_i$,对答案的贡献就是$n_i !$

现在考虑如果有些点已经被填了数要怎么做。

下文说的因数个数,指质因数分解后,去掉重复因数后的序列

考虑位置$x$填了数$y$,那么要满足$x$的因数个数要等于$y$的因数个数,因为本来$x$填的是$x$,现在填了$y$,那么如果有解的话,也要满足对于每个$(i, x) = 1$,$(a_i, y) = 1$。之前已经满足的是$(a_i, x) = 1$;反之也成立。所以当且仅当$x$和$y$的因数个数相同,才能满足这一点。

我们把$x$和$y$的因数按照大小排序,显然就是按照出现次数进行排序了(出现次数=$n/i$)

那么我们要做的就是把$x$和$y$的每个因数进行匹配,那么就是排序后对应的位置进行匹配。显然这个匹配是一个一一对应关系(双射)。为什么这么做呢?相当于我钦定了这两个质数交换(实际上给了$a_i$已经钦定了)

如果有不能对应的,也就是第二个结论:出现次数不相等,那么显然无解。

在这里我还被能不能去掉之前的匹配,和其他重新匹配,类似于匈牙利这种想法卡了一会儿。。。。这个显然没有问题,因为我两边无论什么顺序都要匹配,不能调换使得不在匹配中。

然后如果对应关系出现了冲突,显然一个质数不能和两个同时互换,这情况也是无解。

对于填了数$a_i$,相当于我$a_i$这个位置被钦定改变了,也就是对于第一个结论,会少一部分数可以进行互换,需要扣除。

那么就写完了。

需要强调的是,处理1的问题,我们把1看做一个质数。这样处理起来比较方便。当然也可以特判之类的

把双射写成单射在CodeForces上有两种情况:①wa on 37;②AC。(分别对应两个单射)

CF数据太水了啊。。太垃圾了。

终于调完了

upd:原来的代码只能过单组数据,现在更新了。。

交CF请把N和F开到100w

# include <vector>
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm> using namespace std; typedef long long ll;
typedef unsigned long long ull;
typedef long double ld; const int N = 3e5 + , F = 3e5;
const int mod = 1e9 + ; int n, a[N], c[N], fac[N]; int p[F/], pn, id[F];
bool isnp[F + ];
inline void sieve() {
isnp[] = isnp[] = ;
for (int i=; i<=F; ++i) {
if(!isnp[i]) p[++pn] = i, id[i] = pn;
for (int j=; j<=pn && i*p[j]<=F; ++j) {
isnp[i*p[j]] = ;
if(i%p[j] == ) break;
}
}
} vector<int> y[N];
inline void divide() {
y[].push_back();
for (int i=; i<=pn; ++i)
for (int j=p[i]; j<=F; j+=p[i]) y[j].push_back(p[i]);
} inline void combine(int x) {
c[x] = ;
for (int i=; i<y[x].size(); ++i) c[x] = c[x] * y[x][i];
} int t[N], u[N], fr[N], rf[N]; inline void sol() {
int ans = ;
cin >> n;
for (int i=; i<=n; ++i) scanf("%d", a+i);
memset(t, , sizeof t);
memset(u, , sizeof u);
memset(rf, , sizeof rf);
memset(fr, , sizeof fr);
for (int i=; i<=n; ++i) t[c[i]] ++;
for (int i=; i<=n; ++i)
if(a[i] != ) t[c[i]] --;
for (int i=; i<=n; ++i) ans = 1ll * ans * fac[t[i]] % mod;
for (int i=; i<=n; ++i) t[i] = ;
for (int i=; i<=n; ++i)
for (int j=; j<y[i].size(); ++j)
t[id[y[i][j]]] ++;
for (int i=; i<=n; ++i) {
if(a[i] != ) {
if(y[i].size() != y[a[i]].size()) {
puts("");
return ;
}
for (int j=; j<y[i].size(); ++j) {
int ta = y[i][j], tb = y[a[i]][j]; ta = id[ta], tb = id[tb];
if(t[ta] != t[tb]) {
puts("");
return ;
}
if(fr[ta] && fr[ta] != tb) {
puts("");
return ;
}
fr[ta] = tb;
if(rf[tb] && rf[tb] != ta) {
puts("");
return ;
}
rf[tb] = ta;
}
}
}
for (int i=; i<=pn; ++i)
if(!fr[i]) u[t[i]] ++;
for (int i=; i<=n; ++i)
ans = 1ll * ans * fac[u[i]] % mod;
cout << ans << endl;
} int main() {
freopen("perm.in", "r", stdin);
freopen("perm.out", "w", stdout);
int T; cin >> T;
sieve();
fac[] = ;
for (int i=; i<=F; ++i) fac[i] = 1ll * fac[i-] * i % mod;
divide();
for (int i=; i<=F; ++i) combine(i);
isnp[] = ; p[++pn] = ; id[] = pn;
while(T--) sol();
return ;
}

「6月雅礼集训 2017 Day10」perm(CodeForces 698F)的更多相关文章

  1. 「6月雅礼集训 2017 Day10」quote

    [题目大意] 一个合法的引号序列是空串:如果引号序列合法,那么在两边加上同一个引号也合法:或是把两个合法的引号序列拼起来也是合法的. 求长度为$n$,字符集大小为$k$的合法引号序列的个数.多组数据. ...

  2. 「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)

    原题传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2687 [题目大意] 给出若干区间,求一个区间的大于等于2的子集,使得 |区间并| 和 | ...

  3. 「6月雅礼集训 2017 Day11」delight

    [题目大意] 有$n$天,每天能吃饭.睡觉.什么事也不干 每天吃饭的愉悦值为$e_i$,睡觉的愉悦值为$s_i$,什么都不干愉悦值为0. 要求每连续$k$天都要有至少$E$天吃饭,$S$天睡觉. 求最 ...

  4. 「6月雅礼集训 2017 Day11」jump

    [题目大意] 有$n$个位置,每个位置有一个数$x_i$,代表从$i$经过1步可以到达的点在$[\max(1, i-x_i), \min(i+x_i, n)]$中. 定义$(i,j)$的距离表示从$i ...

  5. 「6月雅礼集训 2017 Day11」tree

    [题目大意] 给出一棵带权树,有两类点,一类黑点,一类白点. 求切断黑点和白点间路径的最小代价. $n \leq 10^5$ [题解] 直接最小割能过..但是树形dp明显更好写 设$f_{x,0/1/ ...

  6. 「6月雅礼集训 2017 Day8」route

    [题目大意] 给出平面上$n$个点,求一条连接$n$个点的不相交的路径,使得转换的方向符合所给长度为$n-2$的字符串. $n \leq 5000$ [题解] 考虑取凸包上一点,然后如果下一个是‘R' ...

  7. 「6月雅礼集训 2017 Day8」gcd

    [题目大意] 定义times(a, b)表示用辗转相除计算a和b的最大公约数所需步骤. 那么有: 1. times(a, b) = times(b, a) 2. times(a, 0) = 0 3. ...

  8. 「6月雅礼集训 2017 Day8」infection

    [题目大意] 有$n$个人,每个人有一个初始位置$x_i$和一个速度$v_i$,你需要选择若干个人来感染一个傻逼病毒. 当两个人相遇(可以是正面和背面),傻逼病毒会传染,求经过无限大时间后,传染完所有 ...

  9. 「6月雅礼集训 2017 Day7」电报

    [题目大意] 有n个岛屿,第i个岛屿有有向发射站到第$p_i$个岛屿,改变到任意其他岛屿需要花费$c_i$的代价,求使得所有岛屿直接或间接联通的最小代价. $1 \leq n \leq 10^5, 1 ...

随机推荐

  1. UVALive - 6869 Repeated Substrings 后缀数组

    题目链接: http://acm.hust.edu.cn/vjudge/problem/113725 Repeated Substrings Time Limit: 3000MS 样例 sample ...

  2. Ubuntu下FileZilla的安装

    FileZilla是一个免费而且开源的FTP客户端软件,共有两种版本:客户端版本.服务器版本.FileZilla有条理的界面和管理多站点的简化方式使得FileZilla Client成为一个方便高效的 ...

  3. Java之I/O流(第1部分)

    Java 中的I/O流: 1. 输入/输出流原理: 如下图所示:在 java 程序中,对于数据的输入/输出操作以“流”(Stream)的方式进行:J2SDK 提供了各种各样的“流”类,用来获取不同种类 ...

  4. iOS- Apple零配置网络协议Bonjour的使用?

    1.前言 这段时间为了解决公司App的网络离线需求,做了个Apple推出的零配置网络协议Bonjour的Test,主要是为了解决iOS设备的IP获取,之前是可以使用socket的广播来实现,但是使用A ...

  5. 【bzoj2060】[Usaco2010 Nov]Visiting Cows拜访奶牛 树形dp

    题目描述 经过了几周的辛苦工作,贝茜终于迎来了一个假期.作为奶牛群中最会社交的牛,她希望去拜访N(1<=N<=50000)个朋友.这些朋友被标号为1..N.这些奶牛有一个不同寻常的交通系统 ...

  6. snmpwalk的报文检测

    1.先用nc起一个监听的端口,然后看报文是不是正确的: 注:nc是一个模拟各种网络协议的东西,模拟服务器.客户端等: 2.触发告警,让他发报文: 3.用nc模拟一个服务端,启动一个udp的端口163: ...

  7. 【以前的空间】link cut tree

    这篇文章讲的很好很详细,但是写了几天后发现似乎是挺残的版本. 2049: [Sdoi2008]Cave 洞穴勘测 3282: Tree 2002: [Hnoi2010]Bounce 弹飞绵羊 1036 ...

  8. [BZOJ2961] 共点圆 [cdq分治+凸包]

    题面 BZOJ传送门 思路 首先考虑一个点$(x_0,y_0)$什么时候在一个圆$(x_1,y_1,\sqrt{x_1^2+y_1^2})$内 显然有:$x_1^2+y_1^2\geq (x_0-x_ ...

  9. 【BZOJ4894】天赋(矩阵树定理)

    [BZOJ4894]天赋(矩阵树定理) 题面 BZOJ Description 小明有许多潜在的天赋,他希望学习这些天赋来变得更强.正如许多游戏中一样,小明也有n种潜在的天赋,但有 一些天赋必须是要有 ...

  10. BZOJ1502:[NOI2005]月下柠檬树——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=1502 https://www.luogu.org/problemnew/show/P4207 李哲 ...