题目描述
一开始有(n)个除了颜色完全相同的小球。
定义使用一次膜法的效果是重新排列第(l_i)个到第(r_i)个小球,(Mogician)变完(m)次膜法后,让(Mengbier)猜最终的顺序。
最终,(Mengbier)全部猜错了。(Mengbier)怀疑(Mogician)作弊了,但他没有证据,所以他只好向你求助。
(Mengbier)给定了(n)个小球的初始状态,以及(m)次膜法的范围(l_i)和(r_i)。
(Mogician)给出了最终的状态,你需要判断(Mogician)是否作弊了,即是否可以从初始状态转移到最终状态。
题解
考虑如果两个小球颜色相同而且可以转移到最终状态的话,那么他们一定可以不改变相对位置就转移到最终状态,证明:假设他们改变了相对位置变成了最终状态,那么他们的路径肯定会有交叉,这样就说明路径不交叉肯定可行,也就是说可以不改变相对位置。
这样的话,我们将小球从(1)到(n)依次对应目标状态它对应的小球,记为(b)数组。
因为从初始状态到达目标状态可行的话,也就相当于从目标状态到达初始状态可行,所以我们将题目要求转化为是否可以从目标状态转化为初始状态。
这样的话就相当于是是否可以将(b)数组变成一个(1)到(n)的有序序列,那么我们每一次操作就相当于排序了,将对应的(l_i)到(r_i)的数从小到大排序,最后扫一遍(b)数组判断。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1005;
int T, n, m, a[N], b[N], c[N], flag;
vector<int> vec[N];
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void init() {flag = 0; for(int i = 1; i <= 1000; i ++) {c[i] = 0; vec[i].clear();}}
void get_b()
{
for(int i = 1; i <= n; i ++)
{
c[a[i]] ++;
if(c[a[i]] > (int)vec[a[i]].size()) {flag = 1; return;}
b[i] = vec[a[i]][c[a[i]] - 1];
}
}
int main()
{
T = read();
while(T -- > 0)
{
n = read(); m = read(); init();
for(int i = 1; i <= n; i ++) a[i] = read();
for(int i = 1, x; i <= n; i ++) {x = read(); vec[x].push_back(i);}
get_b(); if(flag == 1) {puts("NIE"); continue;}
for(int i = 1, l, r; i <= m; i ++)
{
l = read(); r = read();
sort(b + l, b + r + 1);
}
for(int i = 1; i <= n; i ++) if(b[i] != i) {flag = 1; break;}
if(flag == 1) puts("NIE");
else puts("TAK");
}
return 0;
}