Luogu1053 NOIP2005篝火晚会

  首先造出所要求的得到的环。如果将位置一一对应上,答案就是不在所要求位置的人数。因为显然这是个下界,并且脑补一下能构造出方案达到这个下界。

  剩下的问题是找到一种对应方案使错位数最少。可以暴力旋转这个环,然而是n2的。诶是不是特别熟悉……这好像很像卷积?然而好像没有什么优美的函数能方便的计算出两个排列的不匹配数,所以就别想NTT了。(是不是只有我这种弱智会往这上面想啊)

  应该可以类似暴力的移动开头利用偏移量之类的来计算,然而容易被绕晕。注意到每个人向右移动到目标位置的最少次数之间的关系是不变的,相同的总是相同不同的总是不同,直接统计即可。

  注意还可以将环翻转,需要再搞一次。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 50010
#define P 998244353
int n,a[N],b[N],q[N],cnt[N<<1],ans=0;
bool flag[N];
void solve()
{
    memset(cnt,0,sizeof(cnt));
    for (int i=1;i<=n;i++) cnt[(q[i]-i+n)%n]++;
    for (int i=0;i<n;i++) ans=max(ans,cnt[i]);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("1053.in","r",stdin);
    freopen("1053.out","w",stdout);
    const char LL[]="%I64d
";
#else
    const char LL[]="%lld
";
#endif
    n=read();
    for (int i=1;i<=n;i++) a[i]=read(),b[i]=read();
    int x=a[1],from=1;q[1]=1;flag[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (flag[x]) {cout<<-1;return 0;}
        q[i]=x;flag[x]=1;
        if (a[x]==from) from=x,x=b[x];
        else if (b[x]==from) from=x,x=a[x];
        else {cout<<-1;return 0;}
    }
    if (x!=1) {cout<<-1;return 0;}
    solve();
    reverse(q+1,q+n+1);
    solve();
    cout<<n-ans;
    return 0;
}
原文地址:https://www.cnblogs.com/Gloid/p/9800973.html