https://www.bnuoj.com/v3/problem_show.php?pid=51636
如果我们线段树,每次更新都只维护位置i所代表的数字的话,会有一个显然的错误:
1
4
4 3 2 1
3
1 1 3
1 2 4
2 3 4
这组数据答案为5,为什么呢:
初识的f[i] 为4,3,2,1, 位置i 的值为f[f[i]]
所以树的节点 1,2,3,4, 节点值为1,2,3,4
……………1-4
………1-2………3-4
……1……2……3……4
如果我们更新 只更新l,r的节点值v
那么第一次交换,f[]= 2 3 4 1, 节点值v更新为:3 2 1 4
第二次交换以后, f[]=2 1 4 3,节点值更新为:3 2 1 4
显然二次交换是错的,交换后的v应该为 分别为:3 4 1 2 和1 2 3 4
那么我们如果把其他所有的点全部更新一边,也就是我们在比赛中的想法,是必然超时的, 其实这个想都不要想的超时,
那我们需要更新所有的点吗???
在纸上写写看:
5
5 4 3 2 1
3
1 1 3
1 2 5
上面这组数据有两次交换:
初始f []=5 4 3 2 1
初始 v =1 2 3 4 5
一次交换后 f[]=3 4 5 2 1
只更新l,r错误的v= 5 2 1 4 5
而正确的v= 5 2 1 4 3
第二次交换2,5 f[]= 3 1 5 2 4
只更新l,r错误的v = 5 3 1 4 2
正确的 v = 5 3 4 1 2 (1.4不对)
我们可以发现其实错误的v 和正确的v 之间其实并不需要一个On 来更新一次;
除了l,r 更新
我们只需要更新l,r的位置 这个节点
比如一次交换1,3 ,我们更新1,3 这两个点之外 f[]=3 4 5 2 1
还需要更新 g[1]和g[3],即5 和 1
然后重点是需要更新正确的g值,现在的g[3]=1 ,g[5]=3
第二次更新 则应先更新 g[2] 和2 再更新 g[r]和r
f[]=3 1 5 2 4 时
就更新v:5 2 1 4 3 -> v:5 2 1 1 3 -> 5 3 1 1 3
再: v:5 3 1 1 3 -> v: 5 3 4 1 5 -> 5 3 4 1 2 ,最后得到的结果是一毛一样的,完美
后记:但是这尼玛我还是不知道为毛怎么证明更新这两个g,就是正确的,只是知其然,然而不知其所以然。TUT
今天q神来我校,教了我这个证明。
映射单个成环,如果替换一个i,所影响的是 i自己本身和 i环里面前面的一个值。
两个环
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100005;
int f[MAXN],g[MAXN];
struct node
{
int l,r,m;
ll v;
}s[MAXN<<2];
void push_up(int n)
{
s[n].v=s[n<<1].v+s[n<<1|1].v;
}
void build(int l,int r,int n)
{
int m=(l+r)>>1;
s[n].l=l;
s[n].r=r;
s[n].m=m;
if(r==l){
s[n].v=f[f[l]];
return;
}
build(l,m,n<<1);
build(m+1,r,n<<1|1);
push_up(n);
}
void update(int p,int v,int n) //把p位置 的值更新为v
{
if(s[n].l==p && s[n].r==p)
{
s[n].v=v;
return;
}
if(p<=s[n].m)update(p,v,n<<1);
else update(p,v,n<<1|1);
push_up(n);
}
ll query(int l,int r,int n)
{
if(s[n].l==l && s[n].r==r)
return s[n].v;
if(r<=s[n].m)
return query(l,r,n<<1);
if(l>s[n].m)
return query(l,r,n<<1|1);
return query(l,s[n].m,n<<1)+query(s[n].m+1,r,n<<1|1);
}
int main()
{
//freopen("1.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
int n,q;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&f[i]); //f[i] 第i个数字为f[i];
g[f[i]]=i; // g[i] 数字i 的位置
}
build(1,n,1);
scanf("%d",&q);
while(q--)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op==1)
{
swap(f[l],f[r]); //先交换,f[l]=f[r] ,f[r]=f[l]
//l位置的值为f[l], 所以代表的数字就是: f[f[l]] ;
update(g[l],f[f[g[l]]],1);
update(l,f[f[l]],1);//
update(g[r],f[f[g[r]]],1);
update(r,f[f[r]],1);
swap(g[f[l]],g[f[r]]);
}
else
printf("%lld\n",query(l,r,1));
}
}
return 0;
}