T2牛牛的猜球游戏
记忆化+映射做的。
思路应该挺显然的,就是记忆化一下连续操作后的结果。
比如操作为:
1 2 2 3
的时候,我们可以直接记录两次操作后的数组为
1 2 0 3 4 5 6 7 8 9
也就是可以直接存一下第 次操作以后
~
序列的结果,空间是
,看起来非常正解。但是题目要求的是
~
的。
联想前缀和、线段树等,我们很容易想到要用 ~
的结果和
~
的结果做一些类似于 减法 的操作。
即考虑记录第 次操作的逆操作。
也就是假设第 次操作后的数组是
~
的顺序数组,反向映射得到操作前的数组,
map
或者直接普通数组都行。
为什么这样可以,其实道理很简单,最初的数组和现在的数组中的元素存在一一对应的关系,不要管里面的数字的大小关系的话,实际上就是 个不一样的符号经过若干次操作以后的某个排列顺序,把现在当成有序,那最初的就是逆操作的结果。
最后只需要相前缀和查询一样先把数组初始化为 ~
的逆操作后的结果,再做一次
~
的操作就行了,其中
~
的操作中前
~
的操作会让数组变成
~
的顺序数组,之后的操作就是
~
的操作了。
code
#include<map> #include<set> #include<cmath> #include<queue> #include<bitset> #include<vector> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define rep(i,a,b) for(int i = (a);i <= (b);++i) #define per(i,a,b) for(int i = (a);i >= (b);--i) #define mkp std::make_pair typedef long long ll; typedef unsigned long long ull; using std::string;using std::cin;using std::cout; inline bool cmp(int x,int y){return x < y;} /* -fsanitize=undefined */ const int N = 1e6+9; const int inf = 1e9+9; const double eps = 1e-7; int _,n,m,a[2*N][15],b[2*N][15],u,v,opt[2*N][3],tmp[15],ans[15]; int map[10]; int main(){ std::ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #ifdef LOCAL //use "g++ xxx.cpp -DLOCAL" freopen("in.in", "r", stdin); #endif cin >> n >> m; rep(i,0,9) b[0][i] = a[0][i] = i; rep(i,1,n) cin >> opt[i][1] >> opt[i][2]; //记忆化1~i的结果 rep(i,1,n){ rep(j,0,9) a[i][j] = a[i-1][j]; u = opt[i][1] , v = opt[i][2]; std::swap(a[i][u],a[i][v]); } //推出1~i的逆操作的结果 rep(i,1,n){ rep(j,0,9) map[ a[i][j] ] = j; rep(j,0,9) b[i][j] = map[j]; } while(m--){ cin >> u >> v; u--;//把l变成l-1 rep(i,0,9) tmp[i] = b[u][i]; rep(i,0,9) map[ i ] = a[v][i]; rep(i,0,9) ans[i] = tmp[ map[i] ]; rep(i,0,8) cout << ans[i] << " "; cout << ans[9] << "\n"; } return 0; }