Description
Input
输入文件第一行包含一个正整数 k。之后是 k 组测试用例。 每组测试用例的第一行为一个整数 n。接下来 n 行,每行 n 个以空格隔开的数,用来 描述棋盘的初始状态。
Output
输出文件包含 k 行,如果第 i 组测试用例输入的初始局面可以通过有限次操作达到目 标状态,则输出”You still have a chance.”否则输出” You are destined to be single.”(均不含冒号)
Sample Input
2
3
1 2 3
4 5 6
0 7 8
3
1 2 3
4 5 6
0 8 7
Sample Output
You still have a chance.
You are destined to be single.
Data Constraint
对于 30%的测试数据n = 3, k ≤ 10
对于 50%的测试数据n ≤ 10, k ≤ 10
对于 70%的测试数据n ≤ 30, k ≤ 10
对于 100%的测试数据n ≤ 100, k ≤ 10
Solution
这题……总之一句话——乱搞。
首先把矩阵“拉开”变成一条链,可以发现,是否成功与该序列的逆序对数有关。
由于左右交换其相对顺序不变,上下交换改变了 N-1 个数,而 N 为单数。
所以当逆序对数为单数时不可行、偶数时可行(脑补一番即可)。
我用权值线段树处理出逆序对。(或用树状数组、归并排序等)
Code
#include<cstdio>
#include<cstring>
using namespace std;
const int N=101;
int ans;
int a[N*N],f[N*N*4];
inline int read()
{
int X=0,w=1; char ch=0;
while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
inline void insert(int v,int l,int r,int x)
{
if(l==r)
{
f[v]++;
return;
}
int mid=(l+r)>>1;
if(x<=mid) insert(v<<1,l,mid,x); else insert(v<<1|1,mid+1,r,x);
f[v]=f[v<<1]+f[v<<1|1];
}
inline void query(int v,int l,int r,int x,int y)
{
if(l==x && r==y)
{
ans+=f[v];
return;
}
int mid=(l+r)>>1;
if(y<=mid) query(v<<1,l,mid,x,y); else
if(x>mid) query(v<<1|1,mid+1,r,x,y); else
{
query(v<<1,l,mid,x,mid);
query(v<<1|1,mid+1,r,mid+1,y);
}
}
int main()
{
int k=read();
while(k--)
{
int n=read(),m=ans=0;
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x=read();
if(x) a[++m]=x;
}
insert(1,1,m,a[1]);
for(int i=2;i<=m;i++)
{
insert(1,1,m,a[i]);
if(a[i]<m) query(1,1,m,a[i]+1,m);
}
if(ans&1) printf("You are destined to be single.\n");
else printf("You still have a chance.\n");
}
return 0;
}