BNU 51636 Squared Permutation 线段树

Squared Permutation

最近,无聊的过河船同学在玩一种奇怪的名为“小Q的恶作剧”的纸牌游戏。

现在过河船同学手有张牌,分别写着,打乱顺序之后排成一行,位置从左往右按照标号。

接下来小Q同学会给出个操作,分为以下两种:

1.给定,交换从左往右数的第和第张牌,

2.给定,对从左往右数的第张牌,记下位置是这张牌上的数字的牌的数字,询问所有记下的数字加起来的结果。

虽然无聊的过河船同学精通四则运算,但是要完成这么大的计算量还是太辛苦了,希望你能帮他处理这些操作。

Input

第一行是一个正整数,表示测试数据的组数,

对于每组测试数据,

第一行是一个整数

第二行包含一个的排列,其中第个数表示第张牌上的数字,

第三行是一个整数,表示操作数,

接下来行,每行包含三个整数,其中表示操作的类型。

Output

对于每组测试数据,依次输出所有查询操作的结果,每个结果一行。

Sample Input

1
3
1 2 3
3
2 1 2
1 1 3
2 2 3

Sample Output

3
5

Hint

对于样例,

第二次操作后牌上的数字从左往右依次是3,2,1,

第三次操作的结果是位置是第2张牌上的数字的牌的数字加上位置是第3张牌上的数字的牌的数字,也就是第2张牌上的数字加上第1张牌上的数字,结果是5。

Source

题解:

  明显的线段树

  不过要注意的是在交换的的时候,可能影响的位置有4个,多更新就好了

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include<map>
#include<queue>
using namespace std;
const int N = 4e6+10, M = 30005, mod = 1e9 + 7, INF = 1e9+1000;
typedef long long ll;

int n,vis[N],a[N];
int tr[N],l[N],r[N],las[N];
ll  sum[N];
void build(int k,int x,int y) {
    l[k] = x;
    r[k] = y;
    if(x==y) {
        sum[k]=a[a[x]];return ;
    }
    int mid = (x+y)>>1;
    build(k<<1,x,mid);
    build(k<<1|1,mid+1,y);
    sum[k] = sum[k<<1]+sum[k<<1|1];
}
void update(int k,int x,int c) {
    if(x==l[k]&&x==r[k]) {
        sum[k] = c;
        return ;
    }
    int mid = (l[k]+r[k])/2;
    if(x<=mid) update(k<<1,x,c);
    else update(k<<1|1,x,c);
    sum[k] = sum[k<<1]+sum[k<<1|1];
}
ll ask(int k,int s,int t) {
    if(l[k]==s&&t==r[k]) return sum[k];
    int mid = (l[k]+r[k])>>1 ;
    ll ret = 0;
    if(t<=mid) ret = ask(k<<1,s,t);
    else if(s>mid) ret = ask(k<<1|1,s,t);
    else ret = ask(k<<1,s,mid)+ask(k<<1|1,mid+1,t);
    return ret;
}

int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) {
            scanf("%d",&a[i]);
            las[a[i]] = i;
        }
        build(1,1,n);
        int q;
        scanf("%d",&q);
        while(q--) {
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            if(op == 1) {
                int t1 = a[r];
                int t2 = a[l];
                swap(a[l],a[r]);
                update(1,l,a[t1]);
                update(1,r,a[t2]);
                las[a[l]] = l;
                las[a[r]] = r;
                update(1,las[l],a[l]);
                update(1,las[r],a[r]);
            }
            else {
                printf("%lld\n",ask(1,l,r));
            }
        }
    }
}
时间: 2024-08-25 06:55:11

BNU 51636 Squared Permutation 线段树的相关文章

bnu 51636 Squared Permutation(树状数组)(北师16校赛)

最近,无聊的过河船同学在玩一种奇怪的名为"小Q的恶作剧"的纸牌游戏. 现在过河船同学手有张牌,分别写着,打乱顺序之后排成一行,位置从左往右按照标号. 接下来小Q同学会给出个操作,分为以下两种: 1.给定,交换从左往右数的第和第张牌, 2.给定,对从左往右数的第张牌,记下位置是这张牌上的数字的牌的数字,询问所有记下的数字加起来的结果. 虽然无聊的过河船同学精通四则运算,但是要完成这么大的计算量还是太辛苦了,希望你能帮他处理这些操作. Input 第一行是一个正整数,表示测试数据的组数,

BNU 2418 Ultra-QuickSort (线段树求逆序对)

题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=2418 解题报告:就是给你n个数,然后让你求这个数列的逆序对是多少?题目中n的范围是n < 500000,所以,暴力是不行的.还是第一次学会用线段树求逆序数,这种方法的时间复杂度是n * log n,是不是很快呢,利用了线段树查询速度快的优势.具体的方法如下: 这里先说一下,如果输入的n个数不是连续的,也就是说把这n个数按从小到大的顺序排列起来不是连续的话,还要先离散化一下,其实也就是把

uva 11525 - Permutation(线段树)

题目链接:uva 11525 - Permutation 题目大意:给定n和k,n给定的方式为k个si,根据公式计算出n,求一个由1~k组成的长度为k的序列的第n个排序 解题思路:根据公式的性质,等于对于每个位置找当前状态下第si小的数.线段树子节点均为1,维护和,查询时传入参数查找即可. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int ma

[Codeforces 1295E]Permutation Separation(线段树+贪心)

[Codeforces 1295E]Permutation Separation(线段树+贪心) 题面 给出一个排列\(p_1,p_2,...p_n\).初始时你需要选择一个位置把排列分成左右两个.然后在两个序列间移动元素使得左边序列的所有元素都比右边的所有元素小.给出每个元素\(p_i\)从一个序列移动到另一个序列的代价\(a_i\). 分析 显然最后得到的序列是小的数在一边,大的数在另一边.设从值为\(i\)的元素处分开之后移动代价为\(ans_i\). 一开始假设所有数都移到右边序列,那么

BNU 49100超级线段树

超级线段树 Time Limit: 5000ms Memory Limit: 65536KB 64-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Font Size:  +   - Type:   None Graph Theory      2-SAT     Articulation/Bridge/Biconnected Component  

UVA 11525 Permutation ——(线段树,脑筋急转弯)

只要注意到对于譬如:S1*(k-1)! 因为后面k-1个数字的全排列个数刚好是(k-1)!,那么第一个数字就是没有取过的数字的第(S1+1)个即可.取走这个数字以后这个数字就不能再用了,依次类推即可得到整个排列了. 那么随便用线段树维护一下即可. 代码如下: 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #define t_mid (l+r>>1) 5 #define

BNU 28887——A Simple Tree Problem——————【将多子树转化成线段树+区间更新】

A Simple Tree Problem Time Limit: 3000ms Memory Limit: 65536KB This problem will be judged on ZJU. Original ID: 368664-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Type: None None Graph Theory 2-SA

[CSP-S模拟测试]:Permutation(线段树+拓扑排序+贪心)

题目描述 你有一个长度为$n$的排列$P$与一个正整数$K$你可以进行如下操作若干次使得排列的字典序尽量小对于两个满足$|i−j|\geqslant K$且$|P_i−P_j|=1$的下标$i$与$j$,交换$P_i$与$P_j$ 输入格式 第一行包括两个正整数$n$与$K$第二行包括$n$个正整数,第$i$个正整数表示$P_i$ 输出格式 输出一个新排列表示答案输出共$n$行,第$i$行表示$P_i$ 样例 样例输入: 8 34 5 7 8 3 1 2 6 样例输出: 12675348 数据范

hdu 1394 Minimum Inversion Number(这道题改日我要用线段树再做一次哟~)

Problem Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of