题解:P15013 FUN!!
tangzirui1016 · · 题解
为什么我题解区一篇题解都看不懂……
本题解按照思考方向一步一步推导。
::::info[对题目给出的条件的一些思考]
:::info[图的每个点都只有一条出边说明什么?]
说明构造的图构成了一个内向基环树森林。
:::
:::info[每个点往后跳
最终每个点都会跳到它所在的基环树的环上的点。
:::
:::info[此时,如果把
你会惊奇的发现基环树的环不会发生改变,但是不属于环上的点都会挂在它最终应该到的点上。
所以我们把
如果形态是这样的,那么挂在环上的点可以构造完环上节点后再来构造它们,这时可以很简单的构造出来,这样就可以不用管这些点,所以把这些排除,现在只考虑环上的点。
:::
::::
::::info[拼好环]
我们一开始连出来的环(之后叫原环)就是它们保持关系的最小单位,我们不可能去把这些环拆掉,否则就会有些关系满足不了了。
但是如果我们不对原环进行任何操作,那么可能存在构造不出来的情况。
这里就要用到一个神秘的东西来说明了。
:::info[一个长度为
结论就是最终的经过的点是
我不太会证明,你可以手玩几组(毕竟我就是这么玩出来的),然后在网上搜索一下(好像是和同余的最小解有关)。
:::
所以一个环你不去动它,它有可能跳出来的点本质上是几个长度相等的环。
我们可以利用这个性质,把若干长度相同的环拼在一起,就有可能跳成功。
设
:::info[例子]
比如有
但是,把它们拼起来:
就可以满足条件。
:::
:::info[考虑拼出来的环长度为
利用上面的结论,会包含
:::
:::info[反过来,原环长度为
设有
:::
:::info[如何暴力拼环的过程]
其实是完全背包。
对于长度为
最后看它能不能做到刚好用掉
:::
:::info[对于一个
把
:::
::::
::::success[Code]
#include<bits/stdc++.h>
#define YES puts("Yes")
#define NO puts("No")
using namespace std;
const int N=5e5+5;
int t,n,k,a[N];
int head[N],tot;
int state[N],from[N];
bool cycle[N];
int c[N];
struct edge{
int to,next;
};
edge e[N<<1];
int isnotp[N];
vector<int>d[N];
void add(int from,int to){
e[++tot]={to,head[from]};
head[from]=tot;
}
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') f=(ch=='-'?-f:f),ch=getchar();
while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
bool dfs(int u){
state[u]=1;
bool find=false;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(state[v]==0){
from[v]=u;
if(dfs(v)) find=true;
}
else if(state[v]==1){
int len=1;
cycle[v]=1;
for(int i=u;i!=v;i=from[i]) cycle[i]=1,len++;
c[len]++;
find=true;
}
if(find) break;
}
state[u]=2;
return find;
}
bool check(int l,int s){
if(!c[l]) return true;
int ans=1;
for(int v:d[l]){
while(s%v==0) ans*=v,s/=v;
}
return c[l]%ans==0;
}
void clear(){
for(int i=1;i<=n;i++){
state[i]=from[i]=0;
cycle[i]=0;
head[i]=0;
c[i]=0;
}
tot=0;
}
void solve(){
n=read(),k=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) add(i,a[i]);
for(int i=1;i<=n;i++){
if(!state[i]) dfs(i);
}
for(int i=1;i<=n;i++){
if(!cycle[a[i]]){
NO,clear();
return;
}
}
int s=n+k;
for(int i=1;i<=n;i++){
if(!check(i,s)){
NO,clear();
return;
}
}
YES,clear();
}
int main(){
t=read();
isnotp[1]=1;
for(int i=2;i<N;i++){
if(!isnotp[i]){
d[i].push_back(i);
for(int j=i*2;j<N;j+=i){
isnotp[j]=1;
d[j].push_back(i);
}
}
}
while(t--) solve();
cerr<<'\n'<<clock()*1.0/CLOCKS_PER_SEC<<'\n';
return 0;
}
::::