Loading

线段树练习

写了几道题,对线段树的概念有了一定的认识,这里总结下我写的习惯,方便以后完善及复习。

线段树所用的函数:

  • pushup():向上更新父节点
  • build(): 与普通建树方式相同,最后要pushup()(向上更新(父节点的值)
  • update():判断当前区间与给定区间的关系;若是点:找到点更新即可,若是区间:更新到rt即可,只有用到rt子节点,才向下更新。递归向下结束后,在向上的过程中更新父节点。
  • query(): 判断当前rt与给定区间的关系,若用到lazy操作,查询时用到rt子节点则向下更新,递归过程中查询所需的内容
  • putdown():配合lazy操作,向下更新结点。这里需要明白一段区间 [l, r]的 左儿子[l, (l+r)/2],右儿子[(l+r)/2+1, r],由此知左右子区间长度与父节点的区间长度的关系。(1)、关于pushdown()的部分更新感想见G题。

A - 敌兵布阵

单点更新,查询区间和。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define midf(a,b) (((a) + (b))>>1)
const int maxnode = 50010;
struct Node{
    int value;
    int left, right;
}node[maxnode<<2];
void pushup(int rt){
    node[rt].value = node[rt<<1].value+node[rt<<1|1].value;
}
void build(int l ,int r, int rt){
    node[rt].left=l, node[rt].right=r, node[rt].value=0;
    if(l == r){
        scanf("%d", &node[rt].value);
        return;
    }
    int m = midf(l, r);
    build(lson);
    build(rson);
    pushup(rt);
}
//单点修改
void update(int a, int rt, int ans){
    if(node[rt].left==a && node[rt].right==a){
        node[rt].value += ans;
        return;
    }else{
        int m = midf(node[rt].left, node[rt].right);
        if(a <= m) update(a,rt<<1,ans);
        if(a > m)  update(a,rt<<1|1, ans);
        pushup(rt);  //向下找叶子结点军营的过程中,在每层需向上更新
    }
}
//区间查询
LL query(int l, int r, int rt){
    if(node[rt].left>=l && node[rt].right<=r){
        return node[rt].value;
    }
    LL ans = 0;
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m) ans += query(l,r,rt<<1);
    if(r > m)  ans += query(l,r,rt<<1|1);  //找右儿子结点
    return ans;
}
int main(){
//    freopen("in.txt", "r", stdin);
    int tt;
    scanf("%d",&tt);
    for(int ttt=1; ttt<=tt; ttt++){
        printf("Case %d:\n", ttt);
        int n;
        scanf("%d", &n);
        build(1, n, 1);
        char ask[10];
        while(scanf("%s", ask) != EOF){
            if(ask[0] == 'E'){
                break;
            }else if(ask[0] == 'A'){
                int l, c;
                scanf("%d%d", &l, &c);
                update(l, 1, c);
            }else if(ask[0] == 'S'){
                int l, c;
                scanf("%d%d", &l, &c);
                update(l, 1, -c);
            }else{
                int l, r;
                scanf("%d%d", &l, &r);
                printf("%lld\n", query(l, r, 1));
            }
        }
    }
    return 0;
}

B - I Hate It

单点更新,查询区间最大值

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define midf(a,b) (((a) + (b))>>1)
const int maxnode = 200010;
struct Node{
    int value;
    int left, right;
}node[maxnode<<2];
void pushup(int rt){
    node[rt].value = max(node[rt<<1].value, node[rt<<1|1].value);
}
void build(int l, int r, int rt){
    node[rt].left=l, node[rt].right=r, node[rt].value=0;
    if(l == r){
        scanf("%d", &node[rt].value);
        return;
    }
    int m = midf(l, r);
    build(lson); build(rson);
    pushup(rt);
}
void update(int a, int rt, int b){
    if(node[rt].left==a && node[rt].right==a){
        node[rt].value = b;
        return;
    }else{
        int m = midf(node[rt].left, node[rt].right);
        if(a <= m)  update(a, rt<<1, b);
        if(a > m)  update(a, rt<<1|1, b);
        pushup(rt);
    }
}
int query(int l, int r, int rt){
    if(node[rt].left>=l && node[rt].right<=r){
        //这里注意,容易写错
        return node[rt].value;
    }
    int ans = 0;
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m) ans = max(ans, query(l, r, rt<<1));
    if(r > m)  ans = max(ans, query(l, r, rt<<1|1));
    return ans;
}
int main(){
//    freopen("in.txt", "r", stdin);
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF){
        build(1, n, 1);
        for(int i=0; i<m; i++){
            char ch[2];
            scanf("%s", ch);
            if(ch[0] == 'Q'){
                int l, r;
                scanf("%d%d", &l, &r);
                printf("%d\n", query(l, r, 1));
            }else{
                int a, d;
                scanf("%d%d", &a, &d);
                update(a, 1, d);
            }
        }
    }
    return 0;
}

C - RMQ with Shifts

题意:shift(S):对序列S做一次(left “circular shift”)左移循环; query():查询区间最小值。

这里比较难想的是:关于shift()这里如何用线段树操作:按题意循环单点更新。这里需要提前保留第一个的值。
故此题变为:单点更新+查区间最小值

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxnode = 1e5+10;
const int inf = 0x3f3f3f3f;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define midf(a,b) (((a)+(b))>>1)
struct Node{
    int left, right;
    int value;
}node[maxnode<<2];
//这里开空间要注意
void pushup(int rt){
    node[rt].value = min(node[rt<<1].value, node[rt<<1|1].value);
}
void build(int l, int r, int rt){
    node[rt].left=l, node[rt].right=r, node[rt].value=0;
    if(l == r){
        scanf("%d", &node[rt].value);
        return;
    }
    int m = midf(l, r);
    build(lson); build(rson);
    pushup(rt);
}
int query(int l, int r, int rt){
    if(node[rt].left>=l && node[rt].right<=r){
        return node[rt].value;
    }
    int ans = inf, m = midf(node[rt].left, node[rt].right);
    if(l<=m)  ans = min(ans, query(l, r, rt<<1));
    if(r>m)   ans = min(ans, query(l, r, rt<<1|1));
    return ans;
}
void update(int a, int rt, int b){
    if(node[rt].left==a && node[rt].right==a){
        node[rt].value = b;
        return;
    }
    int m = midf(node[rt].left, node[rt].right);
    if(a <= m) update(a, rt<<1, b);
    else  update(a, rt<<1|1, b);
    pushup(rt);
}
int query_node(int a, int rt){
    if(node[rt].left==a && node[rt].right==a){
        return node[rt].value;
    }
    int m = midf(node[rt].left, node[rt].right);
    if(a<=m) return query_node(a, rt<<1);
    else  return query_node(a, rt<<1|1);
}
int main(){
//    freopen("in.txt", "r", stdin);
    int n, q;
    scanf("%d%d", &n, &q);
    build(1, n, 1);
    for(int qq=0; qq<q; qq++){
        char str[40];
        scanf("%s", str);
        if(str[0]=='q'){
            int a=0, b=0, i;
            for(i=6; str[i]!=','; i++){
                a = a*10 + (str[i]-'0');
            }
            for(i=i+1; str[i]!=')'; i++){
                b = b*10 + (str[i]-'0');
            }
            printf("%d\n", query(a, b, 1));
        }else{
            int ch[40], len=0;
            memset(ch, 0, sizeof(ch));
            for(int i=6; str[i]!=')'; i++){
                if(str[i]==','){
                    len++;
                }
                else{
                    ch[len] = ch[len]*10 + (str[i]-'0');
                }
            }
            /*
            cout << "ch[]:   ";
            for(int i=0; i<=len; i++){
                cout << ch[i] << "   ";
            }cout << endl;
            */
            int tmp = query_node(ch[0], 1);
            for(int i=1; i<=len; i++){
                update(ch[i-1], 1, query_node(ch[i], 1));
            }
            update(ch[len], 1, tmp);
        }
    }
    return 0;
}

D - A Simple Problem with Integers

区间内每个值均加相同值更新,查询区间求和

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxnode = 1e5+10;
const int inf = 0x3f3f3f3f;
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define midf(a,b) (((a)+(b))>>1)
struct Node{
    int left, right;
    LL sum, add;
}node[maxnode<<2];

void pushup(int rt){
    node[rt].sum = node[rt<<1].sum + node[rt<<1|1].sum;
}
void pushdown(int rt, int m){
    if(node[rt].add){
        node[rt<<1].add += node[rt].add;
        node[rt<<1|1].add += node[rt].add;
        node[rt<<1].sum += node[rt].add * (m - (m>>1));
        node[rt<<1|1].sum += node[rt].add * (m>>1);
        node[rt].add = 0;
    }
}
void build(int l, int r, int rt){
    node[rt].left=l, node[rt].right=r, node[rt].add=0;
    if(l == r){
        scanf("%lld", &node[rt].sum);
        return;
    }
    int m = midf(l, r);
    build(lson);
    build(rson);
    pushup(rt);
}
/*
 * [l,r]内的每个数均加c, rt是根节点
*/
void update(LL c, int l, int r, int rt){
    if(l<=node[rt].left && node[rt].right<=r){
        node[rt].add += c;
        //add表示[l,r]的累加数,标记未下传
        node[rt].sum += (LL)(node[rt].right-node[rt].left+1)*c;
        //自己更新,并未更新子节点
        return;
    }
    pushdown(rt, node[rt].right-node[rt].left+1);
    //update要用到rt的子节点
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m) update(c, l, r, rt<<1);
    if(m < r)  update(c, l, r, rt<<1|1);
    pushup(rt);
}
LL query(int l, int r, int rt){
    if(l<=node[rt].left && node[rt].right<=r){
        return node[rt].sum;
    }
    pushdown(rt, node[rt].right-node[rt].left+1);
    LL ans = 0;
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m) ans+=query(l, r, rt<<1);
    if(m < r)  ans+=query(l, r, rt<<1|1);
    return ans;
}
int main(){
//    freopen("in.txt", "r", stdin);
    int n,q;
    while(scanf("%d%d", &n, &q) != EOF){
        build(1, n, 1);
        while(q--){
            char str[2];
            scanf("%s", str);
            int a, b, c;
            if(str[0] == 'C'){
                scanf("%d%d%d", &a, &b, &c);
                update(c, a, b, 1);
            }else{
                scanf("%d%d", &a, &b);
                printf("%lld\n", query(a, b, 1));
            }
        }
    }
    return 0;
}

E - Just a Hook

这题开始一直没有看到 You may consider the original hook is made up of cupreous sticks. 这句话,有点懵。

按要求为区间赋值最后求区间和。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxnode = 1e5+10;
const int inf = 0x3f3f3f3f;
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define midf(a,b) (((a)+(b))>>1)
struct Node{
    int left, right;
    LL sum, add;
}node[maxnode<<2];
void pushup(int rt){
    node[rt].sum = node[rt<<1].sum + node[rt<<1|1].sum;
}
void pushdown(int rt, int m){
    if(node[rt].add){
        node[rt<<1].add = node[rt].add;
        node[rt<<1|1].add = node[rt].add;
        node[rt<<1].sum = node[rt].add * (m - (m>>1));
        node[rt<<1|1].sum = node[rt].add * (m>>1);
        node[rt].add = 0;
    }
}
void build(int l, int r, int rt){
    node[rt].left=l, node[rt].right=r, node[rt].add=0;
    if(l == r){
        node[rt].sum = 1;
        return;
    }
    int m = midf(l ,r);
    build(lson);
    build(rson);
}
void update(LL c, int l, int r, int rt){
    if(l <= node[rt].left && node[rt].right<=r){
        node[rt].add = c;
        node[rt].sum = (LL)(node[rt].right - node[rt].left + 1) * c;
        return;
    }
    pushdown(rt, node[rt].right-node[rt].left+1);
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m) update(c, l, r, rt<<1);
    if(m < r)  update(c, l, r, rt<<1|1);
    pushup(rt);
}
LL query(int l, int r, int rt){
    if(l <= node[rt].left && node[rt].right <= r){
        return node[rt].sum;
    }
    pushdown(rt, node[rt].right-node[rt].left+1);
    LL ans = 0;
    int m = midf(node[rt].right, node[rt].left);
    if(l <= m)  ans+=query(l, r, rt<<1);
    if(m < r)   ans+=query(l, r, rt<<1|1);
    return ans;
}
int main(){
//    freopen("in.txt", "r", stdin);
    int tt;
    scanf("%d", &tt);
    for(int ttt=1; ttt<=tt; ttt++){
        int n;  scanf("%d", &n);
        build(1, n, 1);
        int q;  scanf("%d", &q);
        while(q--){
            int l, r, a;
            scanf("%d%d%d", &l, &r, &a);
            update(a, l, r, 1);
        }
        printf("Case %d: The total value of the hook is %d.\n",ttt,query(1, n, 1));
    }
    return 0;
}

F - Count Color

题意:长度为L 的木棒,给T种颜色,两种操作:

  • C:区间赋值
  • P:询问区间内颜色个数。

颜色这里由范围可知用状态压缩或运算处理。这里pushdown()操作向下更新子节点与前面题目不同的是这里是直接区间赋值,因为sum代表的是颜色。
其余的就是细心点,传递更新rt根节点信息时认真点。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxnode = 1e5+10;
const int inf = 0x3f3f3f3f;
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define midf(a,b) (((a)+(b))>>1)
struct Node{
    int left, right;
    int sum, add;
}node[maxnode<<2];
void pushup(int rt){
    node[rt].sum = node[rt<<1].sum | node[rt<<1|1].sum;
}
void pushdown(int rt){
    if(node[rt].left == node[rt].right)return;
    if(node[rt].add){
        node[rt<<1].add = node[rt].add;
        node[rt<<1|1].add = node[rt].add;
        node[rt<<1].sum = node[rt].add;
        node[rt<<1|1].sum = node[rt].add;
        node[rt].add = 0;
    }
}
void build(int l, int r, int rt){
    node[rt].left=l, node[rt].right=r, node[rt].add=0;
    if(l == r){
        node[rt].sum=1;
        return;
    }
    int m = midf(l, r);
    build(lson);
    build(rson);
    pushup(rt);
}
void update(int c, int l ,int r, int rt){
    if(l<=node[rt].left && node[rt].right<=r){
        node[rt].sum = node[rt].add = c;
        return;
    }
    pushdown(rt);
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m)  update(c,l,r, rt<<1);
    if(m < r)   update(c,l,r, rt<<1|1);
    pushup(rt);
}
int query(int l, int r, int rt){
    if(l<=node[rt].left && node[rt].right<=r){
        return node[rt].sum;
    }
    pushdown(rt);
    int m = midf(node[rt].left, node[rt].right);
    int ans = 0;
    if(l <= m) ans |= query(l,r, rt<<1);
    if(m < r)  ans |= query(l,r, rt<<1|1);
    return  ans;
}
int get_num(int c){
    int tmp = 0;
    while(c){
        if(c & 1) tmp++;
        c >>= 1;
    }
    return tmp;
}
int main(){
//    freopen("in.txt", "r", stdin);
    int L, T, O;
    while(scanf("%d%d%d", &L, &T, &O) != EOF){
        build(1, L, 1);
        while(O--){
            char str[2];
            scanf("%s", str);
            if(str[0] == 'C'){
                int l, r, c;
                scanf("%d%d%d", &l, &r, &c);
                if(l>r)swap(l, r);
                update(1<<(c-1), l, r, 1);
            }else{
                int l, r;
                scanf("%d%d", &l, &r);
                if(l>r)swap(l, r);
                printf("%d\n", get_num(query(l, r, 1)));
            }
        }
    }
    return 0;
}

G - Count the Colors(zoj1610)

题意:在[0,8000]的区间里染色[l, r],询问这个区间每种颜色所占的连续(颜色相同视为连续)区间的个数。

这道题看似与上道题目相似,可是坑点太多,写了很久,总是过不了样例。
这里先总结下我没考虑到的问题和思维上出错的地方:

  • 上道题目染色给出的(l, r)代表的是从区间l到区间r,而这道题目中染色(l, r)是代表从点l染色到点r,这里思考错,就gg了。这个问题如何处理:其实不难,从原来维护点到现在维护单位区间,就将这个单位区间视为点。在code时什么时候更新(或查询或build)到点还是到区 间,需要注意。到点(l==r),到区间(l+1 == r)。
  • 如何进行查询?这里写法难易与更新update()的写法有有关。先思考查询我们可以按遍历顺序进行判断前一个区间的颜色与当前区间的颜色是否相同来决定是否计数颜色,这种顺序可以视为先序遍历(这个思路感觉很神奇,反正我没想到)。然后是如何计数的问题,这里就需要对根节点做标记了,这个在3中讨论。
  • 如何更新、计数?这里更新一定要更新到点才停止,查询是到区间才停止。如果更新到区间,会漏掉一种情况有可能就是当前这个区间,也有可能是如(l, m, rt<<1)和(m+1, r, rt<<1|1),这种会漏掉(m, m+1)这个单位区间。至于2中更新时如何对根节点进行标记,这里借助pushdown(),因为lazy操作后,需要用到子节点时,要将信息传递下去,同时标记根节点,可以在这个时候进行标记。我认为这是染色的精髓,当更新区间颜色时,导致左右子区间颜色不同时,进行标记,这其实是对区间的标记,代表其在计数时需要向下遍历。标记-2:左右子区间颜色不同。标记-1:这段区间未染色或者存在未染色的子区间。

这道题目对现在的我来说真的是道很好的题,最然没有独立AC,但是学到了一些东西。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxnode = 8010;
const int inf = 0x3f3f3f3f;
#define LL long long
#define lson l,m,rt<<1
#define rson m,r,rt<<1|1
#define midf(a,b) (((a)+(b))>>1)
struct     Node{
    int left, right;
    int add;
}node[maxnode<<2];
void build(int l, int r, int rt){
    node[rt].left=l, node[rt].right=r, node[rt].add=-1;
    if(l+1 == r){
        return;
    }
    int m = midf(l, r);
    build(lson);
    build(rson);
    //rson = m,r,rt<<1|1
    //因为这里是区间的概念,要保证连续性
}
void pushdown(int rt){
    if(node[rt].add >= 0){
        node[rt<<1|1].add = node[rt<<1].add = node[rt].add;
        node[rt].add = -2;
    }
}
void update(int c, int l, int r, int rt){
    if((l == r) || (node[rt].add == c)){
        return;
    }
    /*
     * 这里剪枝掉两种情况:
     *  1、l==r: 这不是一个区间, 是一个点, 无法染色
     *  2、node[rt].add==c: rt点所在的大区间颜色相同。
    */
    if(l <= node[rt].left && node[rt].right <= r){
        node[rt].add = c;
        return;
    }
    pushdown(rt);
    //这里是染色的精髓,当更新区间颜色时,导致rt的左右子区间
    //颜色不同时,则标记node[rt].add=-2,代表其子区间颜色不同
    //心得:pushdown()在向下更新的同时,其实也可以对根节点做标记
    // 这种标记既可以作为信息已经下传的作用(清除lazy标记的意思吧)
    // 也可以对根进行特殊标记,这种标记的对象其实是区间。
    int m = midf(node[rt].left, node[rt].right);
    if(r <= m) update(c,l,r, rt<<1);
    else if(l >= m) update(c, l, r, rt<<1|1);
    else{
        update(c,lson);
        update(c,rson);
    }
    /*
    if(l <= m) update(c,l,r, rt<<1);
    if(m < r) update(c,l,r, rt<<1|1);
    这里会漏掉一种情况(m, m+1)这个区间
    而上面的else里:rson-->m,r,rt<<1|1
        并非平常的  rson-->m+1,r,rt<<1|1
    */
}
int ans[maxnode], color;
void query(int rt){
    if(node[rt].add >= 0){
        //有区间,这段区内颜色相同
        if(node[rt].add != color){  //与前面区间的颜色比较
            ans[node[rt].add]++;
            color = node[rt].add;
        }
        return;
    }
    if(node[rt].left+1 != node[rt].right){
        //不是一个单位区间时(视单位区间为点)
        query(rt<<1);
        query(rt<<1|1);
    }else{
        // 这个线段没有颜色 或这是一个点
        color = -1;
    }
}
int main(){
//    freopen("in.txt", "r", stdin);
    int n;
    while( scanf("%d", &n) != EOF){
        build(0, 8000, 1);
        //cout << "debug\n";
        for(int i=0; i<n; i++){
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            if(a >= b) continue;
            update(c, a, b, 1);
            //这里a+1则将单位线段视作一个点
        }
        color = -1; memset(ans, 0, sizeof(ans));
        query(1);
        for(int i=0; i<maxnode; i++){
            if(ans[i])
                printf("%d %d\n", i, ans[i]);
        }
        putchar('\n');
    }
    return 0;
}

上面几题内容有 区间加 区间乘 区间赋值 区间替换(赋值) 询问区间和

H - Can you answer these queries?

区间内所有数开根号 + 查询区间和。

因为是所有数,所以这里不用lazy,反而简单。有个剪枝的操作:单个数开根号一定会收敛到1,更新区间时,区间内的值都为1,则剪枝。
这题比上题好写的多,就是题目在l,r设置上有点坑,有l>r的数据。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define LL long long
#define midf(l,r) ((l+r)>>1)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxnode = 1e5+10;
struct Node{
    int left, right;
    LL sum;
}node[maxnode<<2];
void pushup(int rt){
    node[rt].sum = node[rt<<1].sum + node[rt<<1|1].sum;
}
void build(int l, int r, int rt){
    node[rt].left=l, node[rt].right=r;
    if(l == r){
        scanf("%lld", &node[rt].sum);
        return;
    }
    int m = midf(l, r);
    build(lson);  build(rson);
    pushup(rt);
}
void update(int l, int r, int rt){
    if(node[rt].left == node[rt].right){
        node[rt].sum = sqrt(1.0*node[rt].sum);
        return;
    }
    if(l<=node[rt].left && node[rt].right<=r && node[rt].sum==(node[rt].right-node[rt].left+1)){
        return;
    }
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m) update(l,r, rt<<1);
    if(m < r)  update(l,r, rt<<1|1);
    pushup(rt);
}
LL query(int l, int r, int rt){
    if(l <= node[rt].left && node[rt].right<=r){
        return node[rt].sum;
    }
    LL ans = 0;
    int m = midf(node[rt].left, node[rt].right);
    if(l <= m) ans+=query(l,r, rt<<1);
    if(m < r)  ans+=query(l,r, rt<<1|1);
    return ans;
}
int main(){
//    freopen("in.txt", "r", stdin);
    int n, qq=1;
    while(scanf("%d", &n) != EOF){
        printf("Case #%d:\n", qq++);
        build(1, n, 1);
        int tt;  scanf("%d", &tt);
        for(int ttt=1; ttt<=tt; ttt++){
            int op, l, r;
            scanf("%d%d%d", &op, &l, &r);
            if(l>r)swap(l,r);
            if(op){
                printf("%lld\n", query(l, r, 1));
            }else{
                update(l, r, 1);
            }
        }
        putchar('\n');
    }
    return 0;
}

积累一个小知识-------约数个数定理。

1、打表:

/*
 * 约数个数定理 求解约数个数
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int prim[maxn], vis[maxn], tot;
int d[maxn], cnt[maxn], n;
void euler(){
    d[1]=1,  cnt[1]=0, tot=0;
    for(int i=2; i<=n; i++){
        if(!vis[i]){
            prim[++tot] = i;
            d[i] = 2;
            cnt[i] = 1;
        }
        for(int j=1; j<=tot&&prim[j]<=n/i; j++){
            vis[i*prim[j]] = 1;
            if(i % prim[j] == 0){
                cnt[i * prim[j]] = cnt[i] + 1;
                d[i*prim[j]] = d[i]/(cnt[i]+1)*(cnt[i*prim[j]]+1);
                break;
            }
            cnt[i*prim[j]] = 1;
            d[i * prim[j]] = d[i]*2;
        }
    }
}
int main(){
    cin >> n;
    euler();
    int ans =0;
    for(int i=1; i<=n; i++) ans+=d[i];
    cout << ans << endl;
    return 0;
}

2、非打表

/*
 * 约数个数定理 求解约数个数
 * 非打表直接求
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int prim[maxn], vis[maxn], tot;
int d[maxn], cnt[maxn];
int fac_num(int n){
    int ans = 1;
    for(int i=2; i*i<=n; i++){
        int k = 0;
        while(n % i == 0){
            n /= i;
            k++;
        }
        if(k)  ans*=(k+1);
    }
    if(n != 1) ans*=2;
    if(ans == 1){
        if(n == 1) return 1;
        else return 2;
    }
    return ans;
}
int main(){
    freopen("in.txt", "r", stdin);
    int m; cin >> m;
    cout << fac_num(m) << endl;
    return 0;
}

I - SUM and REPLACE

(区间更新)所有数变为约数的个数 + (查询)区间求和

设置一个标记max,代表区间内的最大值(不是用来查询),是用来剪枝(约数个数收敛)。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int prim[maxn], vis[maxn], tot;
int d[maxn], cnt[maxn];
void euler(){
    d[1]=1,  cnt[1]=0, tot=0;
    for(int i=2; i<=maxn; i++){
        if(!vis[i]){
            prim[++tot] = i;
            d[i] = 2;
            cnt[i] = 1;
        }
        for(int j=1; j<=tot&&prim[j]<=maxn/i; j++){
            vis[i*prim[j]] = 1;
            if(i % prim[j] == 0){
                cnt[i * prim[j]] = cnt[i] + 1;
                d[i*prim[j]] = d[i]/(cnt[i]+1)*(cnt[i*prim[j]]+1);
                break;
            }
            cnt[i*prim[j]] = 1;
            d[i * prim[j]] = d[i]*2;
        }
    }
}
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxnode = 3e5+10;
int n, m;
struct     Node{
    LL sum, maxval;
}node[maxnode<<2];
void pushup(int rt){
    node[rt].maxval = max(node[rt<<1].maxval, node[rt<<1|1].maxval);
    node[rt].sum = node[rt<<1].sum + node[rt<<1|1].sum;
}
void build(int l, int r, int rt){
    if(l == r){
        scanf("%d", &node[rt].sum);
        node[rt].maxval = node[rt].sum;
        return;
    }
    int m = (l+r)>>1;
    build(lson);  build(rson);
    pushup(rt);
}
void update(int L, int R, int l, int r, int rt){
    if(node[rt].maxval <= 2){
        return;
    }
    if(l == r){  //更新区间内的所有点
        node[rt].maxval = node[rt].sum = d[node[rt].maxval];
        return;
    }
    int m = (l+r)>>1;
    if(L<=m) update(L, R, lson);
    if(m<R) update(L,R, rson);
    pushup(rt);
}
LL query(int L, int R, int l, int r, int rt){
    if(L<=l && r<=R){
        return node[rt].sum;
    }
    LL ans=0;  int m=(l+r)>>1;
    if(L<=m) ans+=query(L,R, lson);
    if(m<R) ans+=query(L,R, rson);
    return ans;
}
int main(){
    euler();
//    freopen("in.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    build(1, n, 1);
    for(int i=0; i<m; i++){
        int op, l, r;
        scanf("%d%d%d", &op, &l, &r);
        if(op == 1){
            update(l,r, 1, n, 1);
        }else{
            printf("%lld\n", query(l,r, 1, n, 1));
        }
    }
    return 0;
}

上面几题内容有:区间加,区间除(下取整),询问区间和。

还有3个部分待学。

hdu6447 YJJ's Salesman

这个dp很好想,离散化也想到了,但我太菜,从来没写过离散化,大佬队友也因为一点点小问题场上一直没解决,所以这题跪了。

dp方程很好想 dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]+w[i][j]) 更新的时候从上到下,从右到左(与01背包相似),找到离散化后列的最大值。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 1e5+10;
struct Tree{
    int sum;
}tr[maxn<<2];
struct Node{
    int x, y, w;
    bool operator < (const Node &cp) const {
        return (x == cp.x) ? y > cp.y : x < cp.x;
    }
    //x从小到达,y从大到小
}node[maxn];
//单点修改+区间查询最大值
void pushup(int rt){
    tr[rt].sum = max(tr[rt<<1].sum, tr[rt<<1|1].sum);
}
void build(int l, int r, int rt){
    if(l == r){
        tr[rt].sum = 0;
        return;
    }
    int m = (l+r)>>1;
    build(lson); build(rson);
    pushup(rt);
}
void update(int c, int pos, int l, int r, int rt){
    if(l == r){
        tr[rt].sum = c;
        return;
    }
    int m = (l+r)>>1;
    if(pos <= m) update(c, pos, lson);
    else       update(c, pos, rson);
    pushup(rt);
}
int query(int L, int R, int l, int r, int rt){
    if(L <= l && R >= r){
        return tr[rt].sum;
    }
    int m = (l+r)>>1, ans=0;
    if(L <= m) ans = max(ans, query(L, R, lson));
    if(m < R)  ans = max(ans, query(L, R, rson));
    return ans;
}
vector<int> v;
int main(){
    freopen("in.txt", "r", stdin);
    int t; scanf("%d", &t);
    while(t--){
        int n;scanf("%d", &n);
        for(int i=1; i<=n; i++){
            scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].w);
            v.push_back(node[i].y);
        }
        sort(v.begin(), v.end());
        sort(node+1, node+n+1);
        v.erase(unique(v.begin(), v.end()), v.end());
        int cnt = v.size();
        build(0, cnt, 1);
        for(int i=1; i<=n; i++){
            int y = lower_bound(v.begin(), v.end(), node[i].y) - v.begin();
            int ans = query(0, y, 0, cnt, 1) + node[i].w;
            update(ans, y+1, 0, cnt, 1);
        }
        printf("%d\n", query(0, cnt, 0, cnt, 1));
    }
    return 0;
}
posted @ 2018-08-22 22:45  seaupnice  阅读(384)  评论(2编辑  收藏  举报