順列のCodeForces 612E平方根

羅区タイトルページポータルcodeforcesタイトルページポータル

定義\(1 \シムN \)を配置\(\)平方\(A ^ 2 = B \)の場合にのみ場合、(\ FORALL iは[IN \ \ {b_i = A_、1、N] } a_iを\) すなわち\(^ 2 \)がある\(\)([1,2、\ cdots、\ n]は\) にマッピング\(2 \)が得られた時刻を配置しました。今、与えられた\(1 \ SIM N \)配置\(\) 検索\(B \)ように\(A = B ^ 2 \)を、すなわち、検索\(\)の平方根を。複数のソリューション、出力いずれかの場合。解がない場合、出力\(--1 \)

\(nは\ LE10 6 ^ \)

マップ上の対象、グラフ理論への最初の考えを参照してください。\(\ FORALL I \に[1、N-] \) から\(Iは\)する(a_iを\)\有向グラフ得るために、有向エッジを構築する(\をG =(V = [ 1、n個]、E = \ {(X、Y)\ Y = MID A_X \})\) 明らかに、図の特定の構成のために、各ノードの次数、双方の程度ので、いくつかのリングで構成されて構築された\(1 \) 次に、\(\)の正方形の後、各ノードは、その元のノードノードを指すことになる元の点を指摘しました。各リングの四角を検討した後、どうなるでしょう:このリングサイズが奇数の場合は明らかに、それはまだ奇数リングであり、このリングサイズであっても、それはに分割されます\(2 \)元のサイズの半分をリング。

今、私たちは持っている\(\)図のを、それは、変形法に基づく平方根を復元する必要があります。各リングを考えてみましょう、分割\(2 \)例:

  1. サイズは奇数です。それ\(\)図の平方根が奇数リングされている可能性があり、それはスプリットリングアウトでも半分によって引き起こされます。
  2. サイズさえあります。それ(\)\図面の平方根が特異リングすることができない、唯一のサイズ\(4 \)はハーフリングトゲの偶数倍です。

明らかに、状況があるマージする(2 \)\異なるサイズのリングは、互いに独立しているように、私たちは、それぞれのサイズに分けて考えるリストアすることができ、同じサイズのリング合併。ポイント\(2 \)例:

  1. サイズは奇数です。その後、自身のそれぞれの環化のいずれかまたはリングの大きさは、他の合併を見つけることです。明らかに、その減少は無条件で、我々は貪欲に各リングを復元するために、独自の選択(各ノード点のノードは、図ノード点を指すルートノード内のノードです)。
  2. サイズさえあります。このとき、その環の環の各々のサイズは、別のものを見つけるために組み合わせることができます。それは、その後、リングサイズの数が奇数である場合、直接出力オフペアリングすることができない\は、( - 1 \)のままにし、そうでなければ、任意の組み合わせ二十から二は、マージ(合成)2(\ \の両方のリングに任意には、開始点を検索し、\((1,1)\に(2,2&)\に(1,2)\に(2,1)\に(Lは、3)\ cdots \ \する)場所を(\ ((X、Y)\)を表し\(X \)各環の、開始点から始まるの数\(Y \) リングの平方根としてノード)。

各アクセスノードは、通常の数倍である、時間の複雑さがある\(\ mathrm O(N)\)

以下のコードを貼り付けます。

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
void read(int &x){//快读 
    x=0;char c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
void prt(int x){//快写 
    if(x>9)prt(x/10);
    putchar(x%10^48);
}
const int N=1000000;
int n; 
int a[N+1];//平方之后的排列 
bool vis[N+1];//DFS时的标记数组 
vector<int> v;//此SCC(环)中的点 
void dfs(int x){//DFS 
    if(vis[x])return;
    vis[x]=true;
    v.pb(x);
    dfs(a[x]);
}
vector<vector<int> > vv;//所有环 
vector<int> havsz[N+1];//大小为[i]的所有环的编号 
int ans[N+1];//a的平方根 
int main(){
    read(n);
    for(int i=1;i<=n;i++)read(a[i]);
    for(int i=1;i<=n;i++)if(!vis[i]){//找出每个环 
        v.clear();
        dfs(i);
        if(v.size()&1){//如果是奇环直接自己还原 
            vector<int> v0(v.size());//还原之后的环 
            for(int j=0,now=0;j<v.size();j++,(now+=2)%=v.size())v0[now]=v[j];
            for(int j=0;j<v.size();j++)ans[v0[j]]=v0[(j+1)%v.size()];//将还原之后的环用排列的形式存到a的平方根里 
        }
        else vv.pb(v);//否则存下来等到后面找其他环合并 
    }
    for(int i=0;i<vv.size();i++)havsz[vv[i].size()].pb(i); 
    for(int i=2;i<=n;i+=2)//枚举偶数大小 
        if(havsz[i].size()&1)return puts("-1"),0;//不能两两配对 
        else for(int j=0;j<havsz[i].size();j+=2){//枚举环对 
            vector<int> &v0=vv[havsz[i][j]]/*第1个环*/,&v1=vv[havsz[i][j+1]]/*第2个环*/,v2(2*v0.size())/*还原之后的环*/;
            for(int k=0,now=0;k<v0.size();k++,now+=2)v2[now]=v0[k],v2[now+1]=v1[k];
            for(int k=0;k<v2.size();k++)ans[v2[k]]=v2[(k+1)%v2.size()];//将还原之后的环用排列的形式存到a的平方根里
        }
    for(int i=1;i<=n;i++)prt(ans[i]),putchar(' ');//输出答案 
    return 0;
}

おすすめ

転載: www.cnblogs.com/ycx-akioi/p/CodeForces-612E.html