奇数码问题(求证明)
题意
题解
艹,推逆序对推错了,艹
咳咳,依旧是两种做法。
做法1
考虑把整个图化成一条数列。(第二行拼到第一行后面,以此类推)
考虑整个图的逆序对数(\(0\)也考虑上)。
左右交换会使逆序对数\(+/-1\),那么上下交换呢?
由于\(0\)比所有数字都要小,所以肯定会\(+-n\)
那么\(x\)在数列和中间的\(n-1\)个数字会产生多少个逆序对数呢,假设少了\(q\)个逆序对,多了\(p\)个逆序对?
那么\(q+p\)为偶数,\(p-q\)必定为偶数,又因为\(n\)为奇数,所以上下交换也是一个奇数。
那岂不是意味着每次交换逆序对数就会改变,那拿什么判断呢?
但是不难发现\(0\)的移动也是个奇数,两个一加就是偶数,所以只需要把整个图的逆序对数加上\(0\)的位置即可。
但是至于为什么奇偶性质相同一定能到达,我也不知道,求证明。
#include<cstdio>
#include<cstring>
#define N 510000
using namespace std;
int bst[N];
int a[N],b[N],nn;
inline int lowbit(int x){return x&-x;}
void ins(int x)
{
while(x<=nn)
{
bst[x]++;
x+=lowbit(x);
}
}
int findans(int x)
{
int ans=0;
while(x>=1)
{
ans+=bst[x];
x-=lowbit(x);
}
return ans;
}
int n;
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(bst,0,sizeof(bst));
nn=n*n;
int t=0;
long long an1=0,an2=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&a[t+j]);
a[t+j]++;
if(a[t+j]==1)an1+=t+j;
}
t+=n;
}
for(int i=1;i<=nn;i++)
{
an1+=i-1-findans(a[i]);ins(a[i]);
}
memset(bst,0,sizeof(bst));
t=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&a[t+j]);
a[t+j]++;
if(a[t+j]==1)an2+=t+j;
}
t+=n;
}
for(int i=1;i<=nn;i++)
{
an2+=i-1-findans(a[i]);ins(a[i]);
}
if((an1&1)==(an2&1))printf("TAK\n");
else printf("NIE\n");
}
return 0;
}
做法2
判断忽略\(0\)之后的逆序对数是否相同,相同则可以到达,反之不能。
推法和上面差不多,我就不说了。
#include <bits/stdc++.h>
using namespace std;
const int N=510;
int n,m,a[N*N],b[N*N],c[N*N],i,j,k;
long long cnt;
void merge(int a[],int l,int r)
{
if (r-l<1)
return ;
int mid=(l+r)>>1;
merge(a,l,mid);
merge(a,mid+1,r);
int i=l,j=mid+1;
for (int k=l; k<=r; k++)
{
if (j>r || i<=mid && a[i]<=a[j])
b[k]=a[i++];
else
{
cnt+=mid-i+1;
b[k]=a[j++];
}
}
for (int k=l; k<=r; k++)
a[k]=b[k];
}
signed main()
{
ios::sync_with_stdio(false);
// freopen("stdin.in","r",stdin);
// freopen("stdout.out","w",stdout);
while(cin>>n)
{
int ok=0,x;
for (i=1; i<=n*n; i++)
{
cin>>x;
if (x==0)
ok=1;
else
a[i-ok]=x;
}
ok=0;
for (i=1; i<=n*n; i++)
{
cin>>x;
if (x==0)
ok=1;
else
c[i-ok]=x;
}
cnt=0;
memset(b,0,sizeof(b));
merge(a,1,n*n);
long long ans=cnt;
memset(b,0,sizeof(b));
cnt=0;
merge(c,1,n*n);
if ((ans&1)==(cnt&1))
puts("TAK");
else
puts("NIE");
}
return 0;
}
作者:秦淮岸灯火阑珊已退役
链接:https://www.acwing.com/solution/content/847/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。