线段树开新坑:kuangbin带你飞

版权声明:本文为博主原创文章,未经博主允许不得转载。原文所在http://blog.csdn.net/cww97 https://blog.csdn.net/cww97/article/details/77875662

写在最前面的废话

这里I以前的题是暑假刚刚开始的时候在家写的,然后多校一波就荒废了

9月开头回家一波,重新填坑,= =,kuangbin带你飞的pdf,这才一半题,后面还有一波,蓝瘦,慢慢写吧,不写题怎么变的更强

线段树基础here here

单点更新

A:hdu1166敌兵布阵:单点增减,区间sum

cmy曾经这题疯狂TLE

后来发现是因为cin的原因,cin好慢的说

树状数组也可以做,zkw的暴力单点修改也可加速一波

树状数组

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
const int N=50500;
int tree[N],n,x,y;
string st;

void add(int k,int num){
    while (k<=n){
        tree[k]+=num;
        k+=k&-k;
    }
}

int read(int k){
    int sum=0;
    while (k){
        sum+=tree[k];
        k-=k&-k;
    }
    return sum;
}

int main(){
    int T;scanf("%d",&T);
    for (int cas=1;cas<=T;cas++){
        scanf("%d",&n);
        memset(tree,0,sizeof(tree));
        for (int i=1;i<=n;i++){
            scanf("%d",&x);
            add(i,x);
        }
        scanf("\n");
        printf("Case %d:\n",cas);
        int k=0;
        for (;cin>>st&&st[0]!='E';){
            scanf("%d%d",&x,&y);
            if (st[0]=='Q')    printf("%d\n",read(y)-read(x-1));
            else if (st[0]=='A')add(x,y);
            else add(x,-y);
        }
    }
    return 0;
} 

暴力单点修改

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 7;

struct segmentTree
{
    #define lc (t<<1)
    #define rc (t<<1^1)
    int sum[N], M;

    inline void build(int n){
        M = 1;
        for (;M < n;) M <<= 1;
        M--;
        memset(sum, sizeof(sum), 0);
        for (int i = 1+M; i <= n+M; i++){
            scanf("%d", &sum[i]);
        }
        for (int t = M; t >= 1; t--){
            sum[t] = sum[lc] + sum[rc];
        }
    }

    void add(int t, int x){
        for (sum[t+=M]+=x, t>>=1; t; t>>=1){
            sum[t] = sum[lc] + sum[rc];
        }
    }

    int query(int l, int r){
        int ans = 0;
        for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
            if (~l&1) ans += sum[l^1];
            if ( r&1) ans += sum[r^1];
        }
        return ans;
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int _, n, x, y; 
    scanf("%d", &_);
    for (int __ = 1; __<=_;__++){
        printf("Case %d:\n", __);
        scanf("%d", &n);
        T.build(n);
        getchar();
        string st;
        for (;cin >> st && st[0] != 'E';){
            scanf("%d%d", &x, &y);
            if (st[0] == 'A'){
                T.add(x, y);
            }else if (st[0] == 'S'){
                T.add(x, -y);
            }else{
                printf("%d\n", T.query(x, y));
            }
        }
    }
}

B:HDU 1754 I Hate It单点替换,区间rmq

把上面的代码改几行

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 8e5 + 7;

struct segmentTree
{
    #define lc (t<<1)
    #define rc (t<<1^1)
    int ma[N], M;

    inline void build(int n){
        M = 1;
        for (;M < n;) M <<= 1;
        M--;
        memset(ma, sizeof(ma), 0);
        for (int i = 1+M; i <= n+M; i++){
            scanf("%d", &ma[i]);
        }
        for (int t = M; t >= 1; t--){
            ma[t] = max(ma[lc], ma[rc]);
        }
    }

    void update(int t, int x){
        for (ma[t+=M] = x, t>>=1; t; t>>=1){
            ma[t] = max(ma[lc], ma[rc]);
        }
    }

    int query(int l, int r){
        int ans = 0;
        for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
            if (~l&1) ans = max(ans, ma[l^1]);
            if ( r&1) ans = max(ans, ma[r^1]);
        }
        return ans;
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int _, n, m, x, y; 
    for (;~scanf("%d%d", &n, &m);){        
        T.build(n);
        string st;
        for (;m--;){
            cin >>st;
            scanf("%d%d", &x, &y);
            if (st[0] == 'U'){
                T.update(x, y);
            }else{
                printf("%d\n", T.query(x, y));
            }
        }
    }
}

C:HDU 1394 Minimum Inversion Number 逆序对

树状数组舒服

数字插入按顺序插入对应的位置,然后看这个位置后面有多少个数就有多少逆序对

单点修改,区间求和

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7;

struct binaryIndexTree
{
    int val[N], n;

    inline void init(int n){
        this->n = n;
        memset(val, 0, sizeof(val));
    }

    inline void add(int k, int num){
        for (;k <= n;){
            val[k] += num;
            k += k&-k;
        }
    }

    inline int sum(int k){
        int sum = 0;
        for (;k;){
            sum += val[k];
            k -= k&-k;
        }
        return sum;
    }
} T;

int arr[N], n;

int main()
{
    //freopen("in.txt", "r", stdin); 
    for (;~scanf("%d", &n);){
        T.init(n);
        int sum = 0;
        for (int i = 0; i < n; i++){
            scanf("%d", &arr[i]);
            arr[i]++;
            sum += T.sum(n) - T.sum(arr[i] - 1);
            T.add(arr[i], 1);
        }
        int ans = sum;
        for (int i = 0; i < n; i++){
            sum += (n - arr[i]) - (arr[i] - 1);
            ans = min(ans, sum);
        }
        printf("%d\n", ans);
    }
}

D HDU 2795 Billboard单点查询修改

注意:h = min(h, n);

单点查询区间最大值,查询修改一体

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 7;

struct segmentTree
{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, mid, rt<<1
    #define rson mid+1, r, rt<<1^1
    int val[N], M; // M: num of non-leaf nodes
    // have an index of the array x
    // x + M is the index of it in the tree

    inline void build(int n, int w){
        M = 1; while(M<n) M<<=1; M--;
        // leaf nodes with values
        for (int leaf = 1+M; leaf <= n+M; leaf++){
            val[leaf] = w;
        }
        // leaf nodes which is null
        for (int leaf = n+1+M; leaf <= (M<<1^1); leaf++){
            val[leaf] = 0;
        }
        // non-leaf nodes(include root)
        for (int rt = M; rt >= 1; rt--){
            val[rt] = max(val[lc], val[rc]);
        }
    }

    inline void pushUp(int rt){
        val[rt] = max(val[lc], val[rc]);
    }

    inline int query(int x, int rt){
        if (rt > M){
            val[rt] -= x;
            return rt - M;
        }
        int ans = (val[lc] >= x) ? query(x, lc) : query(x, rc);
        pushUp(rt);
        return ans;
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int n, h, w, x; 
    for (;~scanf("%d%d%d", &h, &w, &n);){      
        h = min(h, n);
        T.build(h, w);
        for (;n--;){
            scanf("%d", &x);
            if (T.val[1] < x) puts("-1");
            else printf("%d\n", T.query(x, 1));
        }
    }
    return 0;
}

E POJ 2828 Buy Tickets单点查询修改

跟上题很像,查询修改一体

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 7;

int ans[N], pos[N], jump[N];

struct segmentTree
{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, mid, rt<<1
    #define rson mid+1, r, rt<<1^1
    int val[N], M; // M: num of non-leaf nodes
    // have an index of the array x
    // x + M is the index of it in the tree

    inline void build(int n){
        M = 1; while(M<n) M<<=1; M--;
        // leaf nodes with values
        for (int leaf = 1+M; leaf <= n+M; leaf++){
            val[leaf] = 1;
        }
        // leaf nodes which is null
        for (int leaf = n+1+M; leaf <= (M<<1^1); leaf++){
            val[leaf] = 0;
        }
        // non-leaf nodes(include root)
        for (int rt = M; rt >= 1; rt--){
            val[rt] = val[lc] + val[rc];
        }
    }

    inline void pushUp(int rt){
        val[rt] = val[lc] + val[rc];
    }

    inline void update(int pos, int jump, int rt){
        if (rt > M){
            val[rt]--;
            ans[rt-M] = jump;
            return;
        }
        if (val[lc] >= pos) update(pos, jump, lc);
        else update(pos-val[lc], jump, rc);
        pushUp(rt);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int n;
    for (;~scanf("%d", &n);){
        T.build(n);
        for (int i = 1; i <= n; i++){
            scanf("%d %d", &pos[i], &jump[i]);
            pos[i]++;
        }
        for (int i = n; i >= 1; i--){
            T.update(pos[i], jump[i], 1);
        }
        for (int i = 1; i < n; i++){
            printf("%d ", ans[i]);
        }
        printf("%d\n", ans[n]);
    }
    return 0;
}

F POJ 2886 Who Gets the Most Candies?反素数+单点操作

题意:

n个熊孩子每个人有个数字a[i],首先k号熊孩子出圈,然后第k+a[i]个熊孩子出圈,一个环,可以绕很多圈,如果a[i]为正则顺时针数,反之逆时针,相当于一个变体的约瑟夫游戏,第i个出圈的熊孩子,有f[i]的得分,f[i]为i的因子个数
反正没人看的讲解:

分为两个部分:线段树模拟约瑟夫游戏+寻找1到n范围内因数数量最多的那个ans,约瑟夫游戏只要做到第ans个人出圈就好了

区间和的线段树,每个叶子节点为1,代表一个熊孩子,出圈置为0,

至于因子数量,my math is very poor,所以我搜了题解,看见标题里一群反素数,于是顺势百度了反素数,搜到反素数深度分析,第三道题正好就是这玩意,于是复制粘贴之(划掉),虽然到现在还不知道反素数是个什么玩意

似乎搜到的题解都是打表来解决的因数个数问题,

我真的debug了10个小时,心累

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e7 + 7;
const LL INF = ~0LL;
const int prime[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};

struct child{
    char name[11];
    int val;
    inline void read(){scanf("%s %d\n", name, &val);}
}arr[N];

LL maxNum, ansPos, n;

void dfs(int dep, LL tmp, int num){
    if (dep >= 16) return;
    if (num > maxNum){
        maxNum = num;
        ansPos = tmp;
    }
    if (num == maxNum && ansPos > tmp) ansPos = tmp;
    for (int i = 1; i < 63; i++){
        if (n / prime[dep] < tmp) break;
        dfs(dep+1, tmp *= prime[dep], num*(i+1));
    }
}

struct segmentTree
{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int val[N], M;

    inline void build(int n){
        M = 1; while(M<n) M<<=1; M--;
        for (int leaf = 1+M; leaf <= n+M; leaf++) val[leaf] = 1;
        for (int leaf = n+1+M; leaf <= (M<<1^1); leaf++) val[leaf] = 0;
        for (int rt = M; rt >= 1; rt--) val[rt] = val[lc] + val[rc];
    }

    inline int update(int pos, int rt){
        val[rt]--;
        if (rt > M) return rt - M;
        if (val[lc] >= pos) return update(pos, lc);
        else return update(pos-val[lc], rc);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int &mod = T.val[1];
    for (LL k; ~scanf("%lld%lld\n", &n, &k);){
        for (int i = 1; i <= n; i++) arr[i].read();
        T.build(n);
        ansPos = INF;
        maxNum = 0;
        dfs(0, 1, 1);

        int pos = 0;
        for (int i = 1; i <= ansPos; i++){
            pos = T.update(k, 1);
            //printf("k = %lld, pos = %d, mod = %d\n", k, pos, mod);
            if (mod == 0) break;
            if (arr[pos].val>0) k = (k-1 + arr[pos].val) % mod;
            else k = ((k + arr[pos].val) % mod + mod) % mod;
            if (k == 0) k = mod;
        }
        printf("%s %lld\n", arr[pos].name, maxNum);
    }
    return 0;
}

区间修改

G HDU 1698 Just a Hook屠夫的钩

dota配图好评!!!

区间修改,最后一下查询总sum

因为这个查询就一次,而且还就直接存在根节点,所以就直接输出了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e6 + 7;

struct segmentTree
{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M, sum[N], tag[N];
    // 这里相对于常见的线段树,多了一个M,
    // M的实际含义为非叶子节点的数量,
    // 原数组索引为x,在线段树数组中的索引就是 M + x

    // 因此,build可以改为自底向上的build
    // n个实际有效的叶子节点,M+1-n个空值的叶子节点
    inline void build(int n){
        M = 1; while(M<n) M<<=1; M--;
        memset(tag, 0, sizeof(tag));
        // leaf nodes with values
        for (int leaf = 1+M; leaf <= n+M; leaf++){
            sum[leaf] = 1;
        }
        // leaf nodes which is null
        for (int leaf = n+1+M; leaf <= (M<<1^1); leaf++){
            sum[leaf] = 0;
        }
        // non-leaf nodes(include root)
        for (int rt = M; rt >= 1; rt--){
            sum[rt] = sum[lc] + sum[rc];
        }
    }

    inline void pushUp(int rt){
        sum[rt] = sum[lc] + sum[rc];
    }

    inline void pushDown(int rt, int len){
        if (!tag[rt]) return;
        tag[lc] = tag[rc] = tag[rt];
        sum[lc] = sum[rc] = tag[rt] * (len>>1);
        tag[rt] = 0;
    }

    inline void update(int L, int R, int x, int l, int r, int rt){
        //printf("update(%d, %d, %d, %d, %d, %d)\n", L, R, x, l, r, rt);
        if (L <= l && r <= R){
            tag[rt] = x;
            sum[rt] = (r-l+1) * x;
            return;
        }
        pushDown(rt, r-l+1);
        int m = (l+r) >> 1;
        if (L <= m) update(L, R, x, lson);
        if (m <  R) update(L, R, x, rson);
        pushUp(rt);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int _, n, q, l, r, x;
    scanf("%d", &_);
    for (int __ = 1; __ <= _; __++){
        scanf("%d%d", &n, &q);
        T.build(n);
        for (;q--;){
            scanf("%d%d%d", &l, &r, &x);
            T.update(l, r, x, 1, T.M+1, 1);
        }
        printf("Case %d: The total value of the hook is %d.\n", __, T.sum[1]);
    }
    return 0;
}

H POJ 3468 A Simple Problem with Integers 区间修改和查询

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL N = 5e5 + 7;

struct segmentTree
{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    LL M, sum[N], tag[N];
    // 这里相对于常见的线段树,多了一个M,
    // M的实际含义为非叶子节点的数量,
    // 原数组索引为x,在线段树数组中的索引就是 M + x

    // 因此,build可以改为自底向上的build
    // n个实际有效的叶子节点,M+1-n个空值的叶子节点
    inline void build(LL n){
        M = 1; while(M<n) M<<=1; M--;
        memset(tag, 0, sizeof(tag));
        // leaf nodes with values
        for (LL leaf = M+1; leaf <= n+M; leaf++){
            scanf("%lld", &sum[leaf]);
        }
        // leaf nodes which is null
        for (LL leaf = n+1+M; leaf <= (M<<1^1); leaf++){
            sum[leaf] = 0;
        }
        // non-leaf nodes(include root)
        for (LL rt = M; rt >= 1; rt--){
            sum[rt] = sum[lc] + sum[rc];
        }
    }

    inline void pushUp(LL rt){
        sum[rt] = sum[lc] + sum[rc];
    }

    inline void pushDown(LL rt, LL len){
        if (tag[rt] == 0) return;
        tag[lc] += tag[rt];
        tag[rc] += tag[rt];
        sum[lc] += tag[rt] * (len>>1);
        sum[rc] += tag[rt] * (len>>1);
        tag[rt] = 0;
    }

    inline void update(LL L, LL R, LL x, LL l, LL r, LL rt){
        //prLLf("update(%d, %d, %d, %d, %d, %d)\n", L, R, x, l, r, rt);
        if (L <= l && r <= R){
            tag[rt] += x;
            sum[rt] += (r-l+1) * x;
            return;
        }
        pushDown(rt, r-l+1);
        LL m = (l + r) >> 1;
        if (L <= m) update(L, R, x, lson);
        if (m <  R) update(L, R, x, rson);
        pushUp(rt);
    }

    LL query(LL L, LL R, LL l, LL r, LL rt){
        if (L <= l && r <= R) return sum[rt];
        pushDown(rt, r-l+1);
        LL m = (l + r) >> 1;
        LL ans = 0;
        if (L <= m) ans += query(L, R, lson);
        if (m <  R) ans += query(L, R, rson);
        return ans;
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    LL n, q, l, r, x;
    char ch;
    for (;~scanf("%lld%lld", &n, &q);){
        T.build(n);
        for (;q--;){
            getchar();
            scanf("%c%lld%lld", &ch, &l, &r);
            if (ch == 'Q'){
                printf("%lld\n", T.query(l, r, 1, T.M+1, 1));
            } else {
                scanf("%lld", &x);
                T.update(l, r, x, 1, T.M+1, 1);
            }
        } 
    }
    return 0;
}

I POJ 2528 Mayor’s posters 离散化姿势比较清奇

by cww97

这里写图片描述

因为这个图所以,不能当做左闭右开的线段树来做

and,卡了两个小时是因为

这个傻逼错误

//for (int i = 1; i <= A; i++) {
//  x[a[i]] = i;//啊啊啊啊啊啊啊啊啊啊啊啊啊啊
    //printf("x[%d] = %d\n", a[i], i);
//}

T.build(A);
for (int i = 1; i <= n; i++){
    int L = lower_bound(a+1, a+A+1, l[i]) - a;//x[l[i]]
    int R = lower_bound(a+1, a+A+1, r[i]) - a;//x[r[i]]
    //printf("(%d, %d)\n", L, R);
    T.update(L, R, i, 1, T.M+1,1);
}

妈的离散化忘了用lower_bound,还开数组存

发现错误前怀疑数据

发现错误后被自己蠢哭

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 2e5 + 7;

int l[N], r[N], n;
int a[N], A, x[N];
bool vis[N];
int ans;

struct segmentTree
{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M, col[N];

    inline void build(int n){
        M = 1; while(M<n)M<<=1; M--;
        memset(col, 0, sizeof(col));
    }

    inline void pushDown(int rt){
        if (col[rt] == 0) return;
        col[lc] = col[rc] = col[rt];
        col[rt] = 0;
    }

    inline void update(int L, int R, int x, int l, int r, int rt){
        if (L <= l && r <= R){
            col[rt] = x;
            return;
        }
        pushDown(rt);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, x, lson);
        if (m <  R) update(L, R, x, rson);
    }

    inline int query(int rt){
        if (col[rt] > 0) {
            if (vis[col[rt]]) return 0;
            vis[col[rt]] = 1;
            return 1;
        }
        if (rt > M) return 0;
        int ans = 0;
        ans += query(lc);
        ans += query(rc);
        return ans;
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int _;
    scanf("%d", &_);
    for (;_--;){
        scanf("%d", &n);
        A = 0;
        for (int i = 1; i <= n; i++){
            scanf("%d%d", &l[i], &r[i]);
            a[++A] = l[i];
            a[++A] = r[i];
        }
        sort(a + 1, a + A+1);
        A = unique(a + 1, a + A+1) - (a + 1);
        for (int i = A; i > 1; i--){
            if (a[i-1] + 1 < a[i]) a[++A] = a[i-1] + 1;
        }
        sort(a + 1, a + A+1);

        T.build(A);
        for (int i = 1; i <= n; i++){
            int L = lower_bound(a+1, a+A+1, l[i]) - a;
            int R = lower_bound(a+1, a+A+1, r[i]) - a;
            T.update(L, R, i, 1, T.M+1,1);
        }
        ans = 0;
        memset(vis, 0, sizeof(vis));
        printf("%d\n", T.query(1));
    }
    return 0;
}

这里写图片描述

J POJ 3225 Help with Intervals 双标记线段树

1.关于集合运算的推导规约,知道集合是什么东西就一定会推导!

U:把区间[l,r]覆盖成1
I:把[-∞,l)(r,∞]覆盖成0    
D:把区间[l,r]覆盖成0
C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换
S:[l,r]区间0/1互换

2.倍化区间处理开闭区间的问题。因为普通的线段树实际处理的并非真正的区间,而是一系列点,相当于处理一个向量。这个问题需要处理的是真正的区间,所以应该有一个主导思想就是,把区间点化!不知哪位大牛搞了一个倍增区间出来,实在佩服!对于待处理区间a,b,对其边界均乘2。若区间左开则对左界值+1,若区间右开,则对右界-1!

如:[2,3]会倍增为[4,6],[2,3)会倍增为[4,5],(2,3]会倍增为[5,6],(2,3)将倍增为[5,5],我们这时可以看到,对于普通线段树无法处理的线段如(x,x+1)将被点化为[2*x+1,2*x+1]!这个问题得到比较完美的解决

最后把查找出来的区间逆向倍增操作一下,就可以得到实际的区间以及起开闭情况!

代码中还将用到延迟更新,向子节点更新操作时,这个具体纠结在互换上面,不过仔细想想还是容易理解的,下面代码会有注解!

本题包含区间01赋值和区间01取反

一开始准备tree当做值,lazy当做是否取反的标记

wa了,lj跟我抱怨:你就不能老老实实写个双标记线段树吗

好的,双标记,反正最后我query到所有的叶子节点,两个标记亦或一下就是值,狗拿耗子,猫下岗了,tree后来,看起来是值,其实是另一个标记,不需要pushUp反正没有区间查询

本题略考验代码能力,很容易WA哭(比如我自己(划掉)),百度到的大部分题解都是学的kuangbin的写法,一个update,update里面分5个大if,我的是在main里面if,线段树就两个功能,区间赋值和取反,剩下的交给int main来组织

铭记一点,老老实实写双标记,别搞骚操作

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 131072 + 7;
int ans[N];

struct segTree{
    #define lc rt<<1
    #define rc rt<<1^1
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int tree[N<<1], lazy[N<<1];

    inline void build(){
        memset(lazy, 0, sizeof(lazy));
        memset(tree, 0, sizeof(tree));
    }

    inline void pushDown(int rt){
        if (tree[rt] != -1){ // -1: mixture
            tree[lc] = tree[rc] = tree[rt];
            lazy[lc] = lazy[rc] = 0;
        }
        if (lazy[rt]){
            if (tree[lc] != -1) tree[lc] ^= 1;
            else lazy[lc] ^= 1;
            if (tree[rc] != -1) tree[rc] ^= 1;
            else lazy[rc] ^= 1;
            lazy[rt] = 0;
        }
        tree[rt] = -1;
    }

    void setval(int L, int R, int val, int l, int r, int rt){
        if (L <= l && r <= R) {
            tree[rt] = val;
            lazy[rt] = 0;
            return;
        }
        pushDown(rt);
        int m = (l + r) >> 1;
        if (L <= m) setval(L, R, val, lson);
        if (m <  R) setval(L, R, val, rson);
    }

    void invert(int L, int R, int l, int r, int rt){
        if (L <= l && r <= R){
            if (tree[rt] != -1) tree[rt] ^= 1;
            else lazy[rt] ^= 1;
            return;
        }
        pushDown(rt);
        int m = (l + r) >> 1;
        if (L <= m) invert(L, R, lson);
        if (m <  R) invert(L, R, rson);
    }

    void query(int l, int r, int rt){
        if (l == r){ // leaf
            ans[l] = tree[rt] ^ lazy[rt];
            return;
        }
        pushDown(rt);
        int m = (l + r) >> 1;
        query(lson);
        query(rson);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    char op, lseg, rseg;
    int l, r, n = 131072;
    T.build();
    for (;~scanf("%c %c%d,%d%c\n", &op, &lseg, &l, &r, &rseg);){
        l <<= 1, r <<= 1;
        if (lseg == '(') l++;
        if (rseg == ')') r--;
        //printf("--------> %c [%d, %d]\n", op, l, r);
        if (l > r){
            if (op == 'I' || op == 'C'){
                T.setval(0, n, 0, 0, n, 1);
            }
        } else if (op == 'U'){ // S = S | T
            T.setval(l, r, 1, 0, n, 1);
        } else if (op == 'I'){ // S = S & T
            if (l > 0) T.setval(0, l-1, 0, 0, n, 1);
            if (r < n) T.setval(r+1, n, 0, 0, n, 1);
        } else if (op == 'D'){ // S = S - T
            T.setval(l, r, 0, 0, n, 1);
        } else if (op == 'C'){ // S = T - S
            T.invert(l, r, 0, n, 1);
            if (l > 0) T.setval(0, l-1, 0, 0, n, 1);
            if (r < n) T.setval(r+1, n, 0, 0, n, 1);
        } else { // op == 'S': S = S^T = (S-T)|(T-S)
            T.invert(l, r, 0, n, 1);
        }
    }
    T.query(0, n, 1);
    bool haveAns = 0, haveOne = 0;
    for (int i = 0; i <= n; i++){
        if (!haveOne && ans[i]){
            if (haveAns) putchar(' ');
            haveAns = haveOne = 1;
            putchar(i&1 ? '(' : '[');
            printf("%d,", i >> 1);
        } else if (haveOne && !ans[i]){
            printf("%d", i >> 1);
            putchar((i-1)&1 ? ')' :']');
            haveOne = 0;
        }
    }
    if (!haveAns) puts("empty set");
    else puts("");
    return 0;
}

K POJ 1436 Horizontally Visible Segments 区间染色

思路如同poj2777

不过这题的空间真的好极限

一个bool1字节

8000*8000/1024/1024 = 61MB

剩下的用来开线段树,一步留神就MLE

还有最后那个暴力的三重for循环居然没有TLE

不对我还是TLE了一发,

if (reach[k][i] && reach[k][j]) ans++;

这么写的话就会TLE,大概跟内存读写有一定关系

cpu访问高速缓存里的数据,不直接访问内存里的,所以,这个数组太大(60MB)没法整个丢进高速缓存里,只能一次丢个几行,前一种写法,第三重循环只会访问数组的i和j行,第三重循环每次都是缓存命中的,不会向内存再发请求,而后一种写法,k没变化一次,缓存就不命中一次,就好再装填缓存,然后就TLE了

操作系统真好玩,感谢钱卫宁和高明老师

这题,,太极限

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 8007;

struct line{
    int l, r, x, id;
    inline void read(int i){
        scanf("%d%d%d", &l, &r, &x);
        id = i, l <<= 1, r <<= 1;
    }
    bool operator < (const line &b) const {
        return x < b.x;
    }
} lines[N];

bool reach[N][N];

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int tree[N*8], n;

    inline void build(){
        n = 8000 * 2; // length of tree
        memset(tree, 0, sizeof(tree));
    }

    inline void pushDown(int rt){
        if (!tree[rt]) return;
        tree[lc] = tree[rc] = tree[rt];
        tree[rt] = 0;
    }

    void update(int L, int R, int val, int l, int r, int rt){
        if (L <= l && r <= R){
            tree[rt] = val;
            return;
        }
        pushDown(rt);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (m <  R) update(L, R, val, rson);
    }

    void query(int L, int R, int id, int l, int r, int rt){
        if (tree[rt]){
            reach[tree[rt]][id] = 1;
            return;
        }
        if (l == r) return;
        pushDown(rt);
        int m = (l + r) >> 1;
        if (L <= m) query(L, R, id, lson);
        if (m <  R) query(L, R, id, rson);
    }

    inline void query(int l, int r, int id){
        query(l, r, id, 0, n, 1);
    }
    inline void update(int l, int r, int val){
        update(l, r, val, 0, n, 1);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int _, n;
    scanf("%d", &_);
    for (; _--;){
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) lines[i].read(i);
        sort(lines + 1, lines + n + 1);
        T.build();
        memset(reach, 0, sizeof(reach));
        for (int i = 1; i <= n; i++){
            T.query(lines[i].l, lines[i].r, i);
            T.update(lines[i].l, lines[i].r, i);
        }
        int ans = 0;
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= n; j++) if (reach[i][j]){
                for (int k = 1; k <= n; k++){
                    if (reach[i][k] && reach[j][k]) ans++;
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

L POJ 2991 Crane 计算几何

题意:有一个吊车由很多个不同长度的线段组成,一开始是一条长直线起点在(0,0),尾节点在(0,sum[n]),每条线段之间的夹角的初始值是180度。然后有一些操作a、 b将第a条线段和a+1之间的夹角变成b度,经过每一次操作都要求出尾节点的坐标。

首先要用到一个计算几何的知识(没学过。。请教而来):
对于一个向量x,y,逆时针旋转a度得到的新向量x’、y’,可以由如下推导

[xy][cos(a)sin(a)sin(a)cos(a)]=[xy]

把每个线段认为是一个个向量,整体的endpoint就是所有向量的sum

如下结构体为线段树的节点

struct vec{
    double x, y, a, lazy;// a is the angle between i and i-1
};

很容易读错题的一点是:每次更新给的角度a不是旋转角度,而是s和s+1的夹角

所以旋转角度 ang = vec[s+1].a - a

update之后再更新vec[s+1] = a

线段树区间更新是将s+1到n旋转ang度

还有一个attention是:本题请使用C++提交,G++会TLE

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 4e4 + 7;
const double PI = acos(-1.0);
const double EPS = 1e-8;
int arr[N];

struct vec{
    double x, y, a, lazy;// a is the angle between i and i-1
    vec (){}
    vec (double q, double w, double e, double r):x(q), y(w), a(e), lazy(r){}
    vec operator + (const vec &V) const { // only no-leaf nodes get from +
        return vec(x + V.x, y + V.y, a, 0); // so last 2 numbers no important
    }
    inline void tag(const double &ang){
        double ox = x, oy = y; // old x&y
        x = ox * cos(-ang) - oy * sin(-ang);// rotate
        y = ox * sin(-ang) + oy * cos(-ang);
        lazy += ang;
    }
    inline void print(){ // 0.0 YouYa
        double px = fabs(x)<EPS ? 0 : x; // I don't want to see "-0.00"
        double py = fabs(y)<EPS ? 0 : y;
        printf("%.2lf %.2lf\n", px, py);
    }
};

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M;
    vec tree[N];

    inline void build(const int &n){
        M=1; for(;M<n;)M<<=1; if(M>1)M--;
        memset(tree, 0, sizeof tree);
        for (int len, i = 1; i <= M+1; i++){
            tree[i+M] = vec(0, i<=n ? arr[i] : 0, PI, 0);
        }
        for (int rt = M; rt >= 1; rt--) {
            tree[rt] = tree[lc] + tree[rc];
        }
    }

    inline void pushDown(int rt){
        if (fabs(tree[rt].lazy) < EPS) return;
        tree[lc].tag(tree[rt].lazy);
        tree[rc].tag(tree[rt].lazy);
        tree[rt].lazy = 0;
    }

    void update(int L, int R, double val, int l, int r, int rt){
        if (L <= l && r <= R){tree[rt].tag(val); return;}
        pushDown(rt);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (m <  R) update(L, R, val, rson);
        tree[rt] = tree[lc] + tree[rc];
    }
    inline void update(int l, int r, double val){
        update(l, r, tree[l+M].a - val, 1, M+1, 1);
        tree[l+M].a = val;//attention: val is the ang between l-1 and l
        tree[1].print();
    }
} T; // sometimes we need undef here

int main(){
    //freopen("in.txt", "r", stdin);
    bool firstT = 1;
    for (int n, q; ~scanf("%d%d", &n, &q);){
        if (!firstT) puts(""); firstT = 0;
        for (int i = 1; i <= n; i++) scanf("%d", arr + i);
        T.build(n);
        for (int s, a; q--;){
            scanf("%d%d", &s, &a);
            T.update(s+1, n, PI * a / 180);
        }
    }
    return 0;
}

区间合并

M POJ 3667 Hotel 区间合并

题意:

1 a:询问是不是有连续长度为 a 的空房间,有的话住进最左边

2 a b:将[a,a+b-1]的房间清空

思路:记录区间中最长的空房间

线段树操作:update:区间替换 query:询问满足条件的最左断点

在pushUp的时候合并左右连续的区间,第一次写,看了会kuangbin的写法

用for循环建成标准满二叉树,再define一下lc和rc,写的时候,真是舒服

想到了一个新的,关于线段树的,封装上的优化

    // have relation with int main
    inline void update(int l, int r, int val){
        update(l, r, val, 1, M+1, 1);
    }
    inline int query(int len){
        return query(len, 1, M+1, 1);
    }

没什么卵用,还可能被各位大佬或者压行型选手喷强行增加代码行数,不过这么写的时候,写int main几乎不用动脑子

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 65536 * 2 + 7;

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M;
    int lsum[N], msum[N], rsum[N], lazy[N];

    inline void build(const int &n){
        M=1; for(;M<n;) M<<=1; if(M>1)M--;
        memset(lazy,-1, sizeof lazy);
        for (int i = 1; i <= M+1; i++){
            lsum[i+M] = msum[i+M] = rsum[i+M] = i<=n ? 1 : 0;
        }
        for (int rt = M; rt >= 1; rt--) {
            lsum[rt] = msum[rt] = rsum[rt] = msum[lc] + msum[rc];
        }
    }

    inline void pushUp(int rt, int len){
        lsum[rt] = lsum[lc];
        rsum[rt] = rsum[rc];
        if (lsum[rt] == len>>1) lsum[rt] += lsum[rc];
        if (rsum[rt] == len>>1) rsum[rt] += rsum[lc];
        msum[rt] = max(msum[lc], msum[rc]);
        msum[rt] = max(msum[rt], rsum[lc] + lsum[rc]);
    }

    inline void pushDown(int rt, int len){
        if (lazy[rt] == -1) return;
        lazy[lc] = lazy[rc] = lazy[rt];
        lsum[lc] = msum[lc] = rsum[lc] = lazy[rt] ? 0 : len>>1;
        lsum[rc] = msum[rc] = rsum[rc] = lazy[rt] ? 0 : len>>1;
        lazy[rt] = -1;
    }

    void update(int L, int R, int val, int l, int r, int rt){
        if (L <= l && r <= R){
            lsum[rt] = msum[rt] = rsum[rt] = val ? 0 : r-l+1;
            lazy[rt] = val;
            return;
        }
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (m <  R) update(L, R, val, rson);
        pushUp(rt, r-l+1);
    }
    int query(int len, int l, int r, int rt){
        if (l == r) return l;
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        if (msum[lc] >= len) return query(len, lson);
        else if (rsum[lc] + lsum[rc] >= len) return m-rsum[lc]+1;
        else if (msum[rc] >= len) return query(len, rson);
        return 0;
    }
    // have relation with int main
    inline void update(int l, int r, int val){
        update(l, r, val, 1, M+1, 1);
    }
    inline int query(int len){
        return query(len, 1, M+1, 1);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    for (int n, q; ~scanf("%d%d", &n, &q);){
        T.build(n);
        for (int op, pos, len; q--;){
            scanf("%d", &op);
            if (op == 1){ // query
                scanf("%d", &len);
                printf("%d\n", pos = T.query(len));
                if (pos) T.update(pos, pos+len-1, 1);
            } else { // set
                scanf("%d%d", &pos, &len);
                T.update(pos, pos+len-1, 0);
            }
        }
    }
    return 0;
}

N HDU 3308 LCIS 最大连续上升子序列

这种区间固定的题目,建满满二叉树是非常方便的

尤其是单点修改的时候,o(1)找到要修改点,而不是递归向下

这里直接找到然后log的复杂度pushUp就好了

比近期过的大部分稍微快那么一点,没那么明显,

最上面的是我的

这里写图片描述

样例挺好,过样例应该就能过了,区间合并,不知道非递归咋写,这里就搞个常规的query(只会这么写了)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 262144 + 7;
int arr[N];

struct ZKWsegTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M;
    int lnum[N], rnum[N];
    int lsum[N], msum[N], rsum[N];

    inline void pushUp(int rt, int len){
        lnum[rt] = lnum[lc], rnum[rt] = rnum[rc];
        lsum[rt] = lsum[lc], rsum[rt] = rsum[rc];
        msum[rt] = max(msum[lc], msum[rc]);
        if (rnum[lc] < lnum[rc]){
            if (lsum[rt] == len>>1) lsum[rt] += lsum[rc];
            if (rsum[rt] == len>>1) rsum[rt] += rsum[lc];
            msum[rt] = max(msum[rt], rsum[lc] + lsum[rc]);
        }
    }

    inline void build(const int &n){
        M=1;for(;M<n;)M<<=1;if(M>1)M--;
        for (int i = 1; i <= M+1; i++){
            lsum[i+M] = msum[i+M] = rsum[i+M] = 1;
            lnum[i+M] = rnum[i+M] = i<=n ? arr[i] : 0;
        }
        for (int len = 2, rt = M; rt >= 1; rt--) {
            pushUp(rt, len);
            if ((rt&(rt-1)) == 0) len <<= 1;
        }
    }

    inline void update(int pos, int val){
        lnum[pos+=M] = rnum[pos] = val;
        for (int len=2, rt = pos>>1; rt; rt>>=1, len<<=1){
            pushUp(rt, len);
        }
    }

    int query(int L, int R, int l, int r, int rt){
        if (L <= l && r <= R) return msum[rt];
        int ans = 0;
        int m = (l + r) >> 1;
        if (L <= m) ans = max(ans, query(L, R, lson));
        if (m <  R) ans = max(ans, query(L, R, rson));
        if (rnum[lc] < lnum[rc] && L <= m && m < R){
            ans = max(ans, min(m-L+1, rsum[lc])
                         + min(R - m, lsum[rc]));
        }
        return ans;
    }

    inline int query(int l, int r){
        return query(l, r, 1, M+1, 1);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int _; scanf("%d", &_);
    char op[3];
    for (int n, q; _--;){
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; i++) scanf("%d", arr + i);
        T.build(n);
        for (int x, y; q--;){
            scanf("%s%d%d", op, &x, &y);
            if (op[0] == 'U') T.update(x+1, y);
            else printf("%d\n", T.query(x+1, y+1));
        }
    }
    return 0;
}

O HDU 3397区间合并区间赋值区间取反

注意注意注意:保证对vert操作的时候,lazy必须等于-1
lazy更新后清空vert
vert更新的时候^=1即可

lsum[][2], msum[][2], rsum[][2]
分别维护0和1的连续长度,取反的时候swap

一定要,脑子清晰了写

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 262144 + 7;
int arr[N];

// 0: 将区间[a,b]之间的数全部置为0
// 1: 将区间[a,b]之间的数全部置为1
// 2: 将区间[a,b]之间的 1->0  0->1
// 3: 求区间[a,b]之间1的个数
// 4: 求区间[a,b]之间1的最长连续长度

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M; // number of no-leaf nodes
    int lsum[N][2], msum[N][2], rsum[N][2], nsum[N]; // values
    int vert[N], lazy[N]; // tags

    // 赋值操作,结束后记得清空vert标记
    inline void setTag(const int &rt, const int &val, const int &len){
        nsum[rt] = val ? len : 0;
        lsum[rt][0] = msum[rt][0] = rsum[rt][0] = val ? 0 : len;
        lsum[rt][1] = msum[rt][1] = rsum[rt][1] = val ? len : 0;
        lazy[rt] = val;
        vert[rt] = 0;
    }

    // 对rt节点进行取反操作,swap 0 和 1 的值
    inline void vertTag(const int &rt, const int &len){
        nsum[rt] = len - nsum[rt];
        swap(lsum[rt][0], lsum[rt][1]);
        swap(msum[rt][0], msum[rt][1]);
        swap(rsum[rt][0], rsum[rt][1]);
        vert[rt] ^= 1;
    }

    //区间合并的pushUp大体都这么写
    inline void pushUp(const int &rt, const int &len){
        nsum[rt] = nsum[lc] + nsum[rc];
        for (int i = 0; i < 2; i++){
            lsum[rt][i] = lsum[lc][i];
            rsum[rt][i] = rsum[rc][i];
            if (lsum[rt][i] == len>>1) lsum[rt][i] += lsum[rc][i];
            if (rsum[rt][i] == len>>1) rsum[rt][i] += rsum[lc][i];
            msum[rt][i] = max(msum[lc][i], msum[rc][i]);
            msum[rt][i] = max(msum[rt][i], rsum[lc][i] + lsum[rc][i]);
        }
    }

    // 优先lazy标记,但是不要干扰vert标记
    // vert的时候进入vertTag必须保证lazy==-1
    inline void pushDown(const int &rt, const int &len){
        if (lazy[rt] != -1){
            setTag(lc, lazy[rt], len>>1);
            setTag(rc, lazy[rt], len>>1);
            vert[lc] = vert[rc] = 0;
        }
        if (vert[rt]){
            if (lazy[lc] != -1) setTag(lc, lazy[lc]^1, len>>1);
            else vertTag(lc, len>>1);
            if (lazy[rc] != -1) setTag(rc, lazy[rc]^1, len>>1);
            else vertTag(rc, len>>1);
            vert[rt] = 0;
        }
        lazy[rt] = -1;
    }

    inline void build(const int &n){
        M=1; for(;M<n;) M<<=1; if(M>1)M--;
        memset(vert, 0, sizeof vert);
        memset(lazy,-1, sizeof lazy);
        for (int i = 1; i <= M+1; i++){
            nsum[i+M] = i<=n ? arr[i] : 0;
            lsum[i+M][0] = msum[i+M][0] = rsum[i+M][0] = i<=n ?!arr[i] : 0;
            lsum[i+M][1] = msum[i+M][1] = rsum[i+M][1] = i<=n ? arr[i] : 0;
        }
        for (int rt = M, len = 2; rt >= 1; rt--) {
            pushUp(rt, len);
            if ((rt&(rt-1)) == (!rt)) len <<= 1;//O(1)判断2的整数次幂,dep--
        }
    }

    void update(int L, int R, int val, int l, int r, int rt){
        if (L <= l && r <= R){
            if (val != -1) setTag(rt, val, r-l+1);
            else { // invert
                if (lazy[rt] != -1) setTag(rt, lazy[rt]^1, r-l+1);
                else vertTag(rt, r-l+1);
            }
            return;
        }
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (m <  R) update(L, R, val, rson);
        pushUp(rt, r-l+1);
    }

    int sum(int L, int R, int l, int r, int rt){
        if (L <= l && r <= R) return nsum[rt];
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1, ans = 0;
        if (L <= m) ans += sum(L, R, lson);
        if (m <  R) ans += sum(L, R, rson);
        return ans;
    }

    int query(int L, int R, int l, int r, int rt){
        if (L <= l && r <= R) return msum[rt][1];
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        // 区间合并的查询操作
        int ans = min(m-L+1, rsum[lc][1]) + min(R - m, lsum[rc][1]);
        if (L <= m) ans = max(ans, query(L, R, lson));
        if (m <  R) ans = max(ans, query(L, R, rson));
        return ans;
    }

    // have relation with int main, out API
    inline void setval(const int &l, const int &r, const int &val){
        update(l, r, val, 1, M+1, 1);
    }
    inline void invert(const int &l, const int &r){
        update(l, r,  -1, 1, M+1, 1);
    }
    inline int sum(const int &l, const int &r){
        return sum(l, r, 1, M+1, 1);
    }
    inline int query(const int &l, const int &r){ // continus
        return query(l, r, 1, M+1, 1);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    int _; scanf("%d", &_);
    for (int n, q; _--;){
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; i++) scanf("%d", arr + i);
        T.build(n);
        for (int op, l, r; q--;){
            scanf("%d%d%d", &op, &l, &r);
            l++, r++;
            if (op == 0) T.setval(l, r, 0);
            else if (op == 1) T.setval(l, r, 1);
            else if (op == 2) T.invert(l, r);
            else if (op == 3) printf("%d\n", T.sum(l, r));
            else printf("%d\n", T.query(l, r));
        }
    }
    return 0;
}

这题一定程度考验代码能力,wa的可以稍微看看discuss

这里写图片描述

不过这个数据并没有给我起到什么作用,后来也不知道怎么过的

P HDU 2871内存管理 vector真是神奇

直接拿hotel的板子来改

加个vector来记录各个程序所用内存断,没有程序和程序间的合并操作

upper_bound和lower_bound有点抽风,自己手写一个

#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define fi first
#define se second
using namespace std;
typedef pair <int, int> P;
const int N = 65536 * 2 + 7;

vector <P> G;
vector <P>::iterator it;
inline void printG(){
    for (int i = 0; i < G.size(); i++){
        printf("\t\tG[%d] = [%d, %d]\n", i, G[i].fi, G[i].se);
    }
}

int Search(int l, int r, int val){ // lower_bound
    for (int mid; l < r;){
        mid = (l + r) >> 1;
        if (G[mid].fi <= val) l = mid + 1;
        else r = mid;
    }
    return l;
}

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M, n;
    int lsum[N], msum[N], rsum[N], lazy[N];
    // for this problem

    inline void build(const int &n){
        this->n = n;
        M=1; for(;M<n;) M<<=1; if(M>1)M--;
        memset(lazy,-1, sizeof lazy);
        for (int i = 1; i <= M+1; i++){
            lsum[i+M] = msum[i+M] = rsum[i+M] = i<=n ? 1 : 0;
        }
        for (int rt = M; rt >= 1; rt--) {
            lsum[rt] = msum[rt] = rsum[rt] = msum[lc] + msum[rc];
        }
        G.clear(); // holy, exiting STL
    }

    inline void pushUp(int rt, int len){
        lsum[rt] = lsum[lc];
        rsum[rt] = rsum[rc];
        if (lsum[rt] == len>>1) lsum[rt] += lsum[rc];
        if (rsum[rt] == len>>1) rsum[rt] += rsum[lc];
        msum[rt] = max(msum[lc], msum[rc]);
        msum[rt] = max(msum[rt], rsum[lc] + lsum[rc]);
    }

    inline void pushDown(int rt, int len){
        if (lazy[rt] == -1) return;
        lazy[lc] = lazy[rc] = lazy[rt];
        lsum[lc] = msum[lc] = rsum[lc] = lazy[rt] ? 0 : len>>1;
        lsum[rc] = msum[rc] = rsum[rc] = lazy[rt] ? 0 : len>>1;
        lazy[rt] = -1;
    }

    void update(int L, int R, int val, int l, int r, int rt){
        if (L <= l && r <= R){
            lsum[rt] = msum[rt] = rsum[rt] = val ? 0 : r-l+1;
            lazy[rt] = val;
            return;
        }
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (m <  R) update(L, R, val, rson);
        pushUp(rt, r-l+1);
    }

    int query(int len, int l, int r, int rt){
        if (l == r) return l;
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        if (msum[lc] >= len) return query(len, lson);
        else if (rsum[lc] + lsum[rc] >= len) return m-rsum[lc]+1;
        else if (msum[rc] >= len) return query(len, rson);
        return 0;
    }

    // have relation with int main
    inline void Reset(){
        update(1, n, 0, 1, M+1, 1);
        G.clear();
        puts("Reset Now");
    }

    inline void New(const int &len){
        if (msum[1] < len) puts("Reject New");
        else {
            int pos = query(len, 1, M+1, 1);
            update(pos, pos+len-1, 1, 1, M+1, 1);
            printf("New at %d\n", pos);
            it = upper_bound(G.begin(), G.end(), P(pos, pos));
            G.insert(it, P(pos, pos + len -1));
        }
    }

    inline void Free(const int &val){
        int p = Search(0, G.size(), val) - 1;
        //printf("    debug: p = %d, [%d, %d]\n", p, G[p].l, G[p].r);
        //printG();
        if (p == -1 || G[p].se < val) puts("Reject Free");
        else {
            update(G[p].fi, G[p].se, 0, 1, M+1, 1);
            printf("Free from %d to %d\n", G[p].fi, G[p].se);
            G.erase(G.begin() + p);
            //printG();
        }
    }

    inline void Get(const int &rnk){
        if (rnk > G.size()) puts("Reject Get");
        else printf("Get at %d\n", G[rnk-1].fi);
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    char op[9];
    for (int n, q; ~scanf("%d%d", &n, &q);){
        T.build(n);
        for (int x; q--;){
            scanf("%s", op);
            if (op[0] == 'R') T.Reset();
            else {
                scanf("%d", &x);
                if (op[0] == 'N') T.New(x);
                else if (op[0] == 'F') T.Free(x);
                else T.Get(x);
            }
        }
        puts("");
    }
    return 0;
}

vector似乎比想象中的要快不少,这个vector的写法明显感觉不清真,

是不是用set更好些呢

Q HDU 1540 地道战 似乎都是单点操作

单点修改之后直接pushUp

查询到点然后递归向上时统计ans

for循环更方便^-^

#include <cmath>
#include <stack>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 65536 * 2 + 7;
stack <int> S;

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M;
    int lsum[N], msum[N], rsum[N];

    inline void pushUp(int rt, int len){
        lsum[rt] = lsum[lc];
        rsum[rt] = rsum[rc];
        if (lsum[rt] == len>>1) lsum[rt] += lsum[rc];
        if (rsum[rt] == len>>1) rsum[rt] += rsum[lc];
        msum[rt] = max(msum[lc], msum[rc]);
        msum[rt] = max(msum[rt], rsum[lc] + lsum[rc]);
    }

    inline void build(const int &n){
        M=1; for(;M<n;) M<<=1; if(M>1)M--;
        for (int i = 1; i <= M+1; i++){
            lsum[i+M] = msum[i+M] = rsum[i+M] = i<=n ? 1 : 0;
        }
        for (int rt = M, len = 2; rt >= 1; rt--) {
            pushUp(rt, len);
            if ((rt&(rt-1)) == !rt) len <<= 1;
        }
    }

    void update(int &pos, const int &val){
        lsum[pos+=M] = msum[pos] = rsum[pos] = val;
        for (int rt = pos>>1, len = 2; rt; rt>>=1, len <<= 1){
            pushUp(rt, len);
        }
    }

    int query(const int &pos){
        int ans = msum[pos+M];
        if (!ans) return 0;
        bool fromR = (pos + M) & 1;
        for (int rt=(pos+M)>>1, l=pos, r=pos; rt; fromR=rt&1, rt>>=1){
            if (fromR){
                if (lsum[rc]==ans && ans>=pos-l+1) ans += rsum[lc];
                l -= (r - l + 1);
            } else {
                if (rsum[lc]==ans && ans>=r-pos+1) ans += lsum[rc];
                r += (r - l + 1);
            }
        }
        return ans;
    }
} T;

int main(){
    //freopen("in.txt", "r", stdin);
    char op[7];
    for (int n, q; ~scanf("%d%d", &n, &q);){
        T.build(n);
        for (;!S.empty();) S.pop();
        for (int pos; q--;){
            scanf("%s", op);
            if (op[0] == 'R') {
                pos = S.top(); S.pop();
                T.update(pos, 1);
            } else {
                scanf("%d", &pos);
                if (op[0] == 'Q') printf("%d\n", T.query(pos));
                else {
                    S.push(pos);
                    T.update(pos, 0);
                }
            }
        }
    }
    return 0;
}

R CodeForces 46D Parking Lot 板子稍微改改

直接拿Hotel的板子出来改个int main就好了

做到这题的时候脑子快生锈了,把01搞反了测样例测了好久

线段树区间开n+b+f,注意op=2的时候的x操作的是第x次操作的车出来

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define fi first
#define se second
using namespace std;
typedef pair<int, int> P;
const int N = 65536 * 2 * 2 + 7;

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M;
    int lsum[N], msum[N], rsum[N], lazy[N];

    inline void pushUp(int rt, int len){
        lsum[rt] = lsum[lc];
        rsum[rt] = rsum[rc];
        if (lsum[rt] == len>>1) lsum[rt] += lsum[rc];
        if (rsum[rt] == len>>1) rsum[rt] += rsum[lc];
        msum[rt] = max(msum[lc], msum[rc]);
        msum[rt] = max(msum[rt], rsum[lc] + lsum[rc]);
    }

    inline void pushDown(int rt, int len){
        if (lazy[rt] == -1) return;
        lazy[lc] = lazy[rc] = lazy[rt];
        lsum[lc] = msum[lc] = rsum[lc] = lazy[rt] ? 0 : len>>1;
        lsum[rc] = msum[rc] = rsum[rc] = lazy[rt] ? 0 : len>>1;
        lazy[rt] = -1;
    }

    inline void build(const int &n){
        M=1; for(;M<n;) M<<=1; if(M>1)M--;
        memset(lazy,-1, sizeof lazy);
        for (int i = 1; i <= M+1; i++){
            lsum[i+M] = msum[i+M] = rsum[i+M] = i<=n ? 1 : 0;
        }
        for (int rt = M, len = 2; rt >= 1; rt--) {
            pushUp(rt, len);
            if ((rt&(rt-1)) == (!rt)) len <<= 1;
        }
    }

    void update(int L, int R, int val, int l, int r, int rt){
        if (L <= l && r <= R){
            lsum[rt] = msum[rt] = rsum[rt] = val ? 0 : r-l+1;
            lazy[rt] = val;
            return;
        }
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (m <  R) update(L, R, val, rson);
        pushUp(rt, r-l+1);
    }

    int query(int len, int l, int r, int rt){
        if (l == r) return l;
        pushDown(rt, r-l+1);
        int m = (l + r) >> 1;
        if (msum[lc] >= len) return query(len, lson);
        else if (rsum[lc] + lsum[rc] >= len) return m - rsum[lc] + 1;
        else if (msum[rc] >= len) return query(len, rson);
        return 0;
    }

    // have relation with int main
    inline void update(const int &l, const int &r, const int &val){
        update(l, r, val, 1, M+1, 1);
    }
    inline int query(const int &len){
        return query(len, 1, M+1, 1);
    }
} T;

P car[111];

int main(){
    //freopen("in.txt", "r", stdin);
    for (int n, b, f, q; ~scanf("%d%d%d%d", &n, &b, &f, &q);){
        n += b + f;
        T.build(n);
        for (int op, x, pos, i = 1; i <= q; i++){
            scanf("%d%d", &op, &x);
            if (op == 1){ // query
                if (T.msum[1] < b + x + f) puts("-1");
                else {
                    pos = T.query(b + x + f);
                    car[i] = P(pos + b, pos + b + x - 1);
                    printf("%d\n", pos - 1);
                    T.update(car[i].fi, car[i].se, 1);
                }
            } else T.update(car[x].fi, car[x].se, 0);

        }
    }
    return 0;
}

扫描线

S HDU 1542 Atlantis矩形面积并

经典题

这里写图片描述

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL N = 9999;

struct Seg{
    double l, r, h;  // height
    int s;  // status
    Seg(){}
    Seg(double x, double y, double z, int w): l(x), r(y), h(z), s(w){}
    bool operator < (const Seg & b) const {return h < b.h;}
} seg[N];
double ux[N];
int X, S;  // top of seg[] & ux[]

struct segTree{
    #define lc (rt<<1)
    #define rc (rt<<1^1)
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1^1
    int M, cnt[N];
    double sum[N];

    inline void build(int n){
        M=1; while(M<n)M<<=1; if(M>1)M--;
        memset(sum, 0, sizeof sum);
        memset(cnt, 0, sizeof cnt);
    }

    inline void pushUp(int rt, int l, int r){
        if (cnt[rt]) sum[rt] = ux[r+1] - ux[l];
        else sum[rt] = l==r ? 0 : sum[lc] + sum[rc];
    }

    void update(int L, int R, int x, int l, int r, int rt){
        if (L <= l && r <= R){
            cnt[rt] += x;
            pushUp(rt, l, r);
            return;
        }
        LL m = (l + r) >> 1;
        if (L <= m) update(L, R, x, lson);
        if (m <  R) update(L, R, x, rson);
        pushUp(rt, l, r);
    }

    // for int main
    inline void update(int l, int r, int val){
        update(l, r, val, 1, M+1, 1);
    }
} T;

int Search(double key, int l, int r, double ux[]){
    for (; l <= r;){
        int m = (l + r) >> 1;
        if (ux[m] == key) return m;
        if (ux[m] < key) l = m + 1;
        else r = m -1;
    }
    return -1;
}

int main(){
    //freopen("in.txt", "r", stdin);
    for (int n, _ = 1; ~scanf("%d", &n) && n;){
        printf("Test case #%d\n", _++);
        X = S = 0;
        for (int i = 1; i <= n; i++){
            double l, low, r, high;
            scanf("%lf%lf%lf%lf", &l, &low, &r, &high);
            ux[++X] = l;
            ux[++X] = r;
            seg[++S] = Seg(l, r, low, 1);
            seg[++S] = Seg(l, r, high, -1);
        }
        sort(seg + 1, seg + S+1);
        sort(ux + 1, ux + X+1);
        X = unique(ux + 1, ux + X +1) - ux - 1;
        ux[X+1] = ux[X];
        T.build(X);
        double ans = 0;
        for (int i = 1; i < S; i++){
            int l = Search(seg[i].l, 1, X, ux);
            int r = Search(seg[i].r, 1, X, ux) - 1;
            T.update(l, r, seg[i].s);
            ans += T.sum[1] * (seg[i+1].h - seg[i].h);
        }
        printf("Total explored area: %.2lf\n\n", ans);
    }
    return 0;
}
展开阅读全文

没有更多推荐了,返回首页