The 16th Heilongjiang Provincial Collegiate Programming Contest

2021 黑龙江省赛
A: And RMQ
线段树区间与 ->1、维护区间或,复杂度可大可小
2、吉司机线段树

https://codeforces.com/gym/103107/problem/A
const int maxn = 4e5 + 10;
int a[maxn];
int n, m;
struct seg{
    int t[maxn << 2], orr[maxn << 2];
    #define root 1, 1, n
    #define lr rt << 1
    #define rr rt << 1 | 1
    #define mid (l + r >> 1)
    #define lson lr, l, mid
    #define rson rr, mid + 1, r
    inline void pushup(int rt) {
        t[rt] = max(t[lr], t[rr]);
        orr[rt] = orr[lr] | orr[rr];
    }

    void build(int rt, int l, int r) {
        if(l == r) {
            t[rt] = orr[rt] = a[l];
            return ;
        }
        build(lson); build(rson);
        pushup(rt);
    }
    //维护区间或,显然这个值或区间所有数都是区间的数,那么如果v&区间或不变,代表v比区间或更大,不需要更新
    void ord(int rt, int l, int r, int L, int R, int v) {
        if(l == r) {
            t[rt] = orr[rt] &= v;
            return ;
        }
        if(L <= mid && (orr[lr] & v) < orr[lr])    ord(lson, L, R, v);
        if(R > mid && (orr[rr] & v) < orr[rr])     ord(rson, L, R, v);
        pushup(rt);
    }

    void update(int rt, int l, int r, int pos, int v) {
        if(l == r) {
            t[rt] = orr[rt] = v;
            return ;
        }
        if(pos <= mid)  update(lson, pos, v);
        else update(rson, pos, v);
        pushup(rt);
    }

    int query(int rt, int l, int r, int L, int R) {
        if(l >= L && r <= R) return t[rt];
        int res = 0;
        if(L <= mid)    res = query(lson, L, R);
        if(R > mid)     res = max(res, query(rson, L, R));
        return res;
    }
}T;

signed main() {
    n = rd(), m = rd();
    for(int i = 1; i <= n; ++ i)    a[i] = rd();
    T.build(root);
    for(int i = 1; i <= m; ++ i)    {
        char op[4]; scanf("%s", op);
        int l = rd(), r = rd(), v;
        if(op[0] == 'A') {
            v = rd();   T.ord(root, l, r, v);
        }
        else if(op[0] == 'U') T.update(root, l, r);
        else    printf("%d\n", T.query(root, l, r));
    }
    return 0;
}

D:Doin' Time
最简单区间dp裸题

E: Elastic Search
AC自动机裸题
将所有串插入trie树并建立AC自动机,在建立trie树时使得fa[u]为u的父亲节点。
由fail数组与trie的性质可知,fa[u]与fail[u]皆为结点u对应字符串的一部分。
可以用dp[u]表示以结点u对应的字符串为终点的最长嵌套的长度,
则当前结点u的最长嵌套长度为其对应的父亲结点与对应fail数组取大值、再加上该节点所对应的模式串数量,
\(dp[u]=max(dp[fail[u]],dp[fa[u]])+cntword[u]\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int trie[maxn][26];
int cntword[maxn];
int fail[maxn];
int fa[maxn];
int cnt=0;
int dp[maxn];
int maxx;
void insertwords(string s) {
	int root=0;
	for(int i=0; i<s.size(); i++) {
		int next=s[i]-'a';
		if(!trie[root][next]) {
			trie[root][next]=++cnt;
		}
		fa[trie[root][next]]=root;
		root=trie[root][next];
	}
	cntword[root]++;
}
void getfail() {
	queue<int>q;
	fail[0]=0;
	for(int i=0; i<26; i++) {
		if(trie[0][i]) {
			fail[trie[0][i]]=0;
			q.push(trie[0][i]);
		}
	}
	while(!q.empty()) {
		int now=q.front();
		q.pop();
		for(int i=0; i<26; i++) {
			if(trie[now][i]) {
				fail[trie[now][i]]=trie[fail[now]][i];
				q.push(trie[now][i]);
			} else {
				trie[now][i]=trie[fail[now]][i];
			}
		}
	}
}

int query(string s) {
	int now = 0, res = 0, len = s.size();
	for (int i = 0; i < len; i++) {
		now = trie[now][s[i] - 'a'];
		int temp = now;
		while (temp != 0&&cntword[temp]!=-1) {
			res += cntword[temp];
			cntword[temp] = -1;
			temp = fail[temp];
		}
	}
	return res;
}
int vis[maxn];
void bfs() {
	int root=0;
	queue<int>q;
	q.push(root);
	while(!q.empty()){
		root=q.front();
		q.pop();
		for(int i=0;i<26;i++){
			if(trie[root][i]&&!vis[trie[root][i]]){
				dp[trie[root][i]]=max(dp[fa[trie[root][i]]],dp[fail[trie[root][i]]])+cntword[trie[root][i]];
				q.push(trie[root][i]);
				vis[trie[root][i]]=1;
				maxx=max(maxx,dp[trie[root][i]]);
			}
		}
	}
}
int main() {
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	std::cout.tie(0);
	cnt=0;
	int n;
	string s;
	cin >> n;
	for(int i=0; i<n; i++) {
		cin >> s ;
		insertwords(s);
	}
	getfail();
	bfs();
	cout<<maxx<<endl;
}

F:Function
欧拉筛的运用

G:Go? No
tarjan求割点连的最多桥数

H:Hack DSU!
简单思维

L:Labi-Ribi
典中典贪心,生命值还能是负数,真是典中典
就是打怪兽模型,怪兽生命值\(h_i\),打一只怪攻击力增加\(c_i\),大于等于\(h_i\)才能攻击,问一开始最少攻击力
显然对于\(c_i >= 0\),直接按h小的贪,对于\(c_i < 0\),按\(h_i+c_i\)大的贪即可,最后排序后就是求\(max_{i = 1}^n(h - pre_{~i - 1})\)
q = 1000,可以nq做法,q = 1e5,要离线线段树求区间最大值

struct node{
    int h, c;
    bool operator < (const node &x) const {
        if(c >= 0)  return h < x.h;
        else return h + c > x.h + x.c;
    }
}a[maxn], b[maxn];
int h[maxn];
void run() {
    int n = rd(), acnt = 0, bcnt = 0;
    for(int i = 1; i <= n; ++ i)    h[i] = rd();
    for(int i = 1; i <= n; ++ i)    {
        int ta = rd(), tb = rd(), tc = tb - ta;
        if(tc >= 0) b[bcnt ++] = {h[i], tc};
        else        a[acnt ++] = {h[i], tc};
    }
    sort(a, a + acnt);  sort(b, b + bcnt);
    int ans = -0x3f3f3f3f3f3f3f3f, now = 0;
    for(int i = 0; i < bcnt; ++ i) {
        ans = max(ans, b[i].h - now);
        now += b[i].c;
    }
    for(int i = 0; i < acnt; ++ i) {
        ans = max(ans, a[i].h - now);
        now += a[i].c;
    }
    printf("%lld\n", ans);
    int q = rd();
    for(int i = 1; i <= q; ++ i)    {
        int th = rd(), ta = rd(), tb = rd();
        if(tb - ta >= 0)    {
            node tmp = {th, tb - ta};
            int pos = lower_bound(b, b + bcnt, tmp) - b;
            for(int j = bcnt; j > pos; -- j) b[j] = b[j - 1];
            b[pos] = tmp; bcnt ++;
        }
        else {
            node tmp = {th, tb - ta};
            int pos = lower_bound(a, a + acnt, tmp) - a;
            for(int j = acnt; j > pos; -- j) a[j] = a[j - 1];
            a[pos] = tmp;   acnt ++;
        }
        ans = -0x3f3f3f3f3f3f3f3f, now = 0;
        for(int j = 0; j < bcnt; ++ j) {
            ans = max(ans, b[j].h - now);
            now += b[j].c;
        }
        for(int j = 0; j < acnt; ++ j) {
            ans = max(ans, a[j].h - now);
            now += a[j].c;
        }
        printf("%lld\n", ans);
    }
    return ;
}

更新线段树的写法

struct node{
    int h, c, id;
    bool operator < (const node &a) const {
        if(c >= 0 && a.c >= 0)  return h < a.h;
        else if(c >= 0 || a.c >= 0) return  c > a.c;
        else    return h + c > a.h + a.c;
    }
}a[maxn];

int id[maxn], pos[maxn];
//维护max(h[i] - sufsum[i - 1])
//每个节点有sum[i]是子区间权值v的和,mx[i]是子区间最大值
struct seg{
    int t[maxn << 2], sum[maxn << 2], lz[maxn << 2];
    #define root 1, 1, n + q
    #define lr rt << 1
    #define rr rt << 1 | 1
    #define mid (l + r >> 1)
    #define lson    lr, l, mid
    #define rson    rr, mid + 1, r

    inline void push(int rt) {
        t[rt] = max(t[lr], t[rr]);
        sum[rt] = sum[lr] + sum[rr];
    }
    inline void upd(int rt, int v) {
        lz[rt] += v;
        t[rt] += v;
    }
    inline void down(int rt) {
        if(!lz[rt]) return ;
        upd(lr, lz[rt]);    upd(rr, lz[rt]);
        lz[rt] = 0;
    }
    void build(int rt, int l, int r) {
        if(l == r) {
            t[rt] = -0x3f3f3f3f3f3f3f3f;
            sum[rt] = 0;    return ;
        }
        build(lson); build(rson);
        push(rt);
    }

    void update(int rt, int l, int r, int pos, int v) {
        if(l == r) {
            t[rt] = v;
            sum[rt] = a[id[l]].c;
            return ;
        }
        down(rt);
        if(pos <= mid)  update(lson, pos, v);
        else            update(rson, pos, v);
        push(rt);
    }

    void update(int rt, int l, int r, int L, int R, int v) {
        if(L > R)   return ;
        if(l >= L && r <= R) return upd(rt, v), void();
        down(rt);
        if(L <= mid)    update(lson, L, R, v);
        if(R > mid)     update(rson, L, R, v);
        push(rt);
    }

    int query(int rt, int l, int r, int L, int R) {
        if(L > R)   return 0;
        if(l >= L && r <= R)    return sum[rt];
        down(rt);
        int res = 0;
        if(L <= mid)    res += query(lson, L, R);
        if(R > mid)     res += query(rson, L, R);
        return res;
    }
}T;

void run() {
    int n = rd(), t1, t2;
    for(int i = 1; i <= n; ++ i) a[i].h = rd();
    for(int i = 1; i <= n; ++ i) t1 = rd(), t2 = rd(), a[i].c = t2 - t1, id[i] = i;
    int q = rd();
    for(int i = 1; i <= q; ++ i)
        a[n + i].h = rd(), t1 = rd(), t2 = rd(), a[n + i].c = t2 - t1, id[n + i] = n + i;
    for(int i = 1; i <= n + q; ++ i)    a[i].id = i;
    sort(id + 1, id + 1 + n + q, [](int x, int y) {return a[x] < a[y];});
    for(int i = 1; i <= n + q; ++ i)    pos[id[i]] = i;
    T.build(root);
    for(int i = 1; i <= n; ++ i) {
        int npos = pos[i];
        T.update(root, npos, a[i].h - T.query(root, 1, npos - 1));
        T.update(root, npos + 1, n + q, -a[i].c);
    }   printf("%lld\n", T.t[1]);
    for(int i = 1; i <= q; ++ i) {
        int npos = pos[i + n];
        T.update(root, npos, a[i + n].h - T.query(root, 1, npos - 1));
        T.update(root, npos + 1, n + q, -a[i + n].c);
        printf("%lld\n", T.t[1]);
    }
    return ;
}

posted @ 2021-07-16 20:57  wlhp  阅读(274)  评论(0)    收藏  举报