HNOI2016做题笔记

刚刚把HNOI大集合删了因为加题实在太不方便

HNOI2016

D1T1 最小公倍数 (分块、并查集)

看到这种不能用数据结构(实际上是可以用K-D Tree的)维护的题目就应该想到分块然后使用并查集维护连通性和一个连通块中的\(maxa , maxb\)啊QAQ

为了区分询问的\(ab\)与边权的\(ab\),询问的\(ab\)描述变为\(AB\)

对于所有边按照\(a\)从小到大排序并\(\sqrt{M}\)分块。设第\(i\)块的左右端点为\([l_i,r_i]\),那么在询问中找出\(A \in [a_{l_i} , a_{r_i})\)的所有询问,并按照\(B\)从小到大排序,一个个做询问。

对于当前块的第\(j\)个询问,有两种边可以对这个询问造成贡献:

①前\(i-1\)块满足\(b \leq B_j\)的边。注意到排序之后\(B_j\)是递增的,所以将前\(i-1\)块按照\(b\)从小到大排序,并用一个指针维护\(b \leq B_j\)的边

②第\(i\)块中满足\(a \leq A_j , b \leq B_j\)的边。这些边总共只有\(\sqrt{M}\)条可以暴力去做。可是注意到:可能存在对于之前的询问满足条件,但是对于现在的询问不满足条件的边。但是因为有①的边的存在,又不能暴力重建并查集,那么我们的并查集必须要支持撤销。所以使用按秩合并并查集,每一次将所有②操作造成的修改扔进一个栈内,询问完成之后栈序撤销,就能保证复杂度。

总复杂度\(O(q\sqrt{M}logN)\)

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<cassert>
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    while(!isdigit(c) && c != EOF)
        c = getchar();
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = a * 10 + c - 48;
        c = getchar();
    }
    return a;
}

const int MAXN = 5e4 + 3;
struct thing{
    int s , t , A , B , ind;
}now[MAXN << 1] , que[MAXN];
int fa[MAXN] , sz[MAXN] , maxA[MAXN] , maxB[MAXN] , st[500][3];
int N , M , T , Q , top;
vector < thing > pot , cur , tmp;
bool ans[MAXN];

bool operator <(thing a , thing b){
    return a.A < b.A;
}

bool cmp(thing a , thing b){
    return a.B < b.B;
}

void init(){
    for(int i = 1 ; i <= N ; ++i){
        fa[i] = i;
        sz[i] = 1;
        maxA[i] = maxB[i] = -1;
    }
}

inline int find(int a){
    while(fa[a] != a) a = fa[a];
    return a;
}

void mge(int a , int b , int A , int B , bool f = 0){
    a = find(a);
    b = find(b);
    if(sz[a] < sz[b])
        a ^= b ^= a ^= b;
    if(f){
        st[++top][0] = b;
        st[top][1] = maxA[a];
        st[top][2] = maxB[a];
    }
    if(a != b) sz[a] += sz[b];
    fa[b] = a;
    maxA[a] = max(maxA[a] , max(maxA[b] , A));
    maxB[a] = max(maxB[a] , max(maxB[b] , B));
}

void clear(){
    while(top){
        if(fa[st[top][0]] != st[top][0])
            sz[fa[st[top][0]]] -= sz[st[top][0]];
        maxA[fa[st[top][0]]] = st[top][1];
        maxB[fa[st[top][0]]] = st[top][2];
        fa[st[top][0]] = st[top][0];
        --top;
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("out","w",stdout);
#endif
    N = read();
    M = read();
    T = sqrt(M);
    for(int i = 1 ; i <= M ; ++i){
        now[i].s = read();
        now[i].t = read();
        now[i].A = read();
        now[i].B = read();
    }
    Q = read();
    for(int i = 1 ; i <= Q ; ++i){
        que[i].s = read();
        que[i].t = read();
        que[i].A = read();
        que[i].B = read();
        que[i].ind = i;
    }
    sort(now + 1 , now + M + 1);
    sort(que + 1 , que + Q + 1);
    int p = 0 , q = 1;
    ++M;
    now[M].s = now[M].t = 1;
    now[M].A = now[M].B = 0x7fffffff;
    while(p < M){
        init();
        int p1 = p;
        while(p1 < M && p1 - p != T)
            ++p1;
        while(q <= Q && que[q].A < now[p1].A)
            cur.push_back(que[q++]);
        sort(cur.begin() , cur.end() , cmp);
        int pnt = 0;
        for(int i = 0 ; i < cur.size() ; ++i){
            while(pnt < pot.size() && pot[pnt].B <= cur[i].B){
                mge(pot[pnt].s , pot[pnt].t , pot[pnt].A , pot[pnt].B);
                ++pnt;
            }
            for(int j = p ; j < p1 && now[j].A <= cur[i].A ; ++j)
                if(now[j].B <= cur[i].B)
                    mge(now[j].s , now[j].t , now[j].A , now[j].B , 1);
            int s = find(cur[i].s);
            ans[cur[i].ind] = s == find(cur[i].t) && maxA[s] == cur[i].A && maxB[s] == cur[i].B;
            clear();
        }
        sort(now + p + 1 , now + p1 + 1 , cmp);
        tmp.clear();
        int pPot = 0;
        while(pPot < pot.size() || p != p1)
            if(p != p1 && (pPot == pot.size() || pot[pPot].B > now[p + 1].B))
                tmp.push_back(now[++p]);
            else
                tmp.push_back(pot[pPot++]);
        pot = tmp;
        cur.clear();
    }
    for(int i = 1 ; i <= Q ; ++i)
        puts(ans[i] ? "Yes" : "No");
    return 0;
}

D1T2 网络 (整体二分、树状数组)

比较裸的整体二分题目

check中按照时间顺序模拟操作,对于比当前二分值大的添加和删除操作在树状数组上维护链并,对于一次询问查询这个点的子树的权值和是否不为\(0\)

#include<bits/stdc++.h>
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    bool f = 0;
    char c = getchar();
    while(c != EOF && !isdigit(c)){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    while(c != EOF && isdigit(c)){
        a = (a << 3) + (a << 1) + (c ^ '0');
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 100010;
struct query{
    int ind , l , r , LCA , wei , place;
}now[MAXN << 1] , potL[MAXN << 1] , potR[MAXN << 1];
struct Ed{
    int end , upEd;
}Ed[MAXN << 1];
int jump[MAXN][20] , head[MAXN] , dep[MAXN] , dfn[MAXN] , ans[MAXN] , size[MAXN] , Tree[MAXN];
int ts , N , M , cntEd , cntQ;

inline void addEd(int a , int b){
    Ed[++cntEd].end = b;
    Ed[cntEd].upEd = head[a];
    head[a] = cntEd;
}

void dfs(int now , int fa){
    size[now] = 1;
    dfn[now] = ++ts;
    dep[now] = dep[fa] + 1;
    jump[now][0] = fa;
    for(int i = 1 ; i <= 19 ; i++)
        jump[now][i] = jump[jump[now][i - 1]][i - 1];
    for(int i = head[now] ; i ; i = Ed[i].upEd)
        if(Ed[i].end != fa){
            dfs(Ed[i].end , now);
            size[now] += size[Ed[i].end];
        }
}

inline int jumpToLCA(int x , int y){
    if(dep[x] < dep[y])
        swap(x , y);
    for(int i = 19 ; i >= 0 ; i--)
        if(dep[x] - (1 << i) >= dep[y])
            x = jump[x][i];
    if(x == y)
        return y;
    for(int i = 19 ; i >= 0 ; i--)
        if(jump[x][i] != jump[y][i]){
            x = jump[x][i];
            y = jump[y][i];
        }
    return jump[x][0];
}

inline int lowbit(int x){
    return x & -x;
}

inline void add(int now , int num){
    if(!now)
        return;
    while(now <= N){
        Tree[now] += num;
        now += lowbit(now);
    }
}

inline int get(int x){
    int sum = 0;
    while(x){
        sum += Tree[x];
        x -= lowbit(x);
    }
    return sum;
}

void solve(int ql , int qr , int l , int r){
    if(ql > qr)
        return;
    if(l == r){
        do{
            if(now[ql].ind == 3)
                ans[now[ql].place] = l;
        }while(++ql <= qr);
        return;
    }
    int mid = l + r >> 1 , cntL = 0 , cntR = 0 , cnt = 0;
    for(int i = ql ; i <= qr ; ++i)
        if(now[i].ind == 1)
            if(now[i].wei > mid){
                int t = jumpToLCA(now[i].l , now[i].r);
                add(dfn[now[i].l] , 1);
                add(dfn[now[i].r] , 1);
                add(dfn[t] , -1);
                add(dfn[jump[t][0]] , -1);
                potR[++cntR] = now[i];
                ++cnt;
            }
            else
                potL[++cntL] = now[i];
        else
            if(now[i].ind == 2)
                if(now[i].wei > mid){
                    int t = now[i].LCA;
                    add(dfn[now[i].l] , -1);
                    add(dfn[now[i].r] , -1);
                    add(dfn[t] , 1);
                    add(dfn[jump[t][0]] , 1);
                    potR[++cntR] = now[i];
                    --cnt;
                }
                else
                    potL[++cntL] = now[i];
            else
                if(get(dfn[now[i].l] + size[now[i].l] - 1) - get(dfn[now[i].l] - 1) == cnt)
                    potL[++cntL] = now[i];
                else
                    potR[++cntR] = now[i];
    for(int i = 1 ; i <= cntR ; ++i)
        if(potR[i].ind == 1){
            int t = potR[i].LCA;
            add(dfn[potR[i].l] , -1);
            add(dfn[potR[i].r] , -1);
            add(dfn[t] , 1);
            add(dfn[jump[t][0]] , 1);
        }
        else
            if(potR[i].ind == 2){
                int t = potR[i].LCA;
                add(dfn[potR[i].l] , 1);
                add(dfn[potR[i].r] , 1);
                add(dfn[t] , -1);
                add(dfn[jump[t][0]] , -1);
            }
    memcpy(now + ql , potL + 1 , sizeof(query) * cntL);
    memcpy(now + ql + cntL , potR + 1 , sizeof(query) * cntR);
    solve(ql , ql + cntL - 1 , l , mid);
    solve(ql + cntL , qr , mid + 1 , r);
}

int main(){
    N = read();
    M = read();
    int maxT = 0;
    for(int i = 1 ; i < N ; i++){
        int a = read() , b = read();
        addEd(a , b);
        addEd(b , a);
    }
    dfs(1 , 0);
    for(int i = 1 ; i <= M ; i++)
        if((now[i].ind = read() + 1) == 1){
            now[i].l = read();
            now[i].r = read();
            now[i].LCA = jumpToLCA(now[i].l , now[i].r);
            now[i].wei = read();
            maxT = max(maxT , now[i].wei);
        }
        else
            if(now[i].ind == 2){
                now[i] = now[read()];
                now[i].ind = 2;
            }
            else{
                now[i].place = ++cntQ;
                now[i].l = read();
            }
    solve(1 , M , 0 , maxT);
    for(int i = 1 ; i <= cntQ ; i++)
        printf("%d\n" , ans[i] ? ans[i] : -1);
    return 0;
}

D1T3 (主席树、倍增)

D2T1 序列 (单调栈、线段树)

D2T2 矿区 (平面图、向量)

D2T3 大数 (莫队)

不妨将大数看做一个\(base=10\)的哈希

\(pre_i\)表示前\(i\)个数构成的大数的值,\(num_{i,j}\)表示第\(i\)\(j\)个数构成的大数的值,那么有\(num_{i,j} = pre_j - pre_i \times 10^{j-i} = 10^j(\frac{pre_j}{10^j} - \frac{pre_i}{10^i})\)

如果\(P \not\mid 10\),那么\(10^j \not\equiv 0 \mod P\),且在\(\mod P\)意义下存在\(10^{-1}\)。那么把所有的\(\frac{pre_i}{10^i}\)存起来离散化,然后使用莫队统计结果。

如果\(P \mid 10\),那么只需要一个数的末位是\(P\)的倍数,这个数就可以满足条件。

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<cstring>
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    while(!isdigit(c))
        c = getchar();
    while(isdigit(c)){
        a = a * 10 + c - 48;
        c = getchar();
    }
    return a;
}

#define ll long long
const int MAXN = 1e5 + 7;
int lsh[MAXN] , val[MAXN] , Hash[MAXN] , poww10[MAXN] , inv10[MAXN] , cnt[MAXN];
int N , M , T , cntL , MOD;
ll ans[MAXN];
ll cur;
char s[MAXN];
struct query{
    int l , r , ind;
    bool operator <(const query a)const{
        return l / T == a.l / T ? r < a.r : l < a.l;
    }
}now[MAXN];

inline int poww(long long a , int b){
    int times = 1;
    while(b){
        if(b & 1)
            times = times * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return times;
}

inline char getc(){
    char c = getchar();
    while(!isdigit(c))
        c = getchar();
    return c;
}

void init(){
    poww10[0] = inv10[0] = 1;
    for(int i = 1 ; i <= N ; ++i)
        poww10[i] = poww10[i - 1] * 10ll % MOD;
    inv10[1] = poww(10 , MOD - 2);
    for(int i = 2 ; i <= N ; ++i)
        inv10[i] = 1ll * inv10[i - 1] * inv10[1] % MOD;
}

inline void add(int pos){cur += cnt[Hash[pos]]++;}

inline void del(int pos){cur -= --cnt[Hash[pos]];}

inline void add2(int pos){if(s[pos] % MOD == 0)cur += pos , ++cnt[0];}

inline void del2(int pos){if(s[pos] % MOD == 0)cur -= pos , --cnt[0];}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("out","w",stdout);
#endif
    MOD = read();
    scanf("%s" , s + 1);
    N = strlen(s + 1);
    T = sqrt(N);
    init();

    for(int i = 1 ; i <= N ; ++i){
        val[i] = (val[i - 1] * 10ll + (s[i] -= 48)) % MOD;
        lsh[i] = Hash[i] = 1ll * val[i] * inv10[i] % MOD;
    }
    sort(lsh , lsh + N + 1);
    cntL = unique(lsh , lsh + N + 1) - lsh - 1;
    for(int i = 0 ; i <= N ; ++i)
        Hash[i] = lower_bound(lsh , lsh + cntL + 1 , Hash[i]) - lsh;

    M = read();
    for(int i = 1 ; i <= M ; ++i){
        now[i].l = read();
        now[i].r = read();
        now[i].ind = i;
    }
    sort(now + 1 , now + M + 1);

    int L = 1 , R = 0;
    if(10 % MOD)
        add(0);
    for(int i = 1 ; i <= M ; ++i){
        while(L > now[i].l)
            10 % MOD ? add((--L) - 1) : add2(--L);
        while(R < now[i].r)
            10 % MOD ? add(++R) : add2(++R);
        while(L < now[i].l)
            10 % MOD ? del((L++) - 1) : del2(L++);
        while(R > now[i].r)
            10 % MOD ? del(R--) : del2(R--);
        ans[now[i].ind] = 10 % MOD ? cur : cur - 1ll * cnt[0] * (L - 1);
    }
    for(int i = 1 ; i <= M ; ++i)
        cout << ans[i] << '\n';
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Itst/p/10408599.html