A层邀请赛2

终于快填完坑了,只差一个进化了

A. inversions

发现各个长度中的逆序对个数互不影响,而某个长度内的逆序对个数只会是正/反数列中逆序对个数,其只与长度大于等于它的区间翻转次数有关,所以用归并排序预处理每个长度对应的逆序对数,每次O(n)累加即可

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = (1 << 20) + 55;
typedef unsigned long long ull;
typedef long long ll;
int a[maxn], q[maxn], n ,m, threshold, cnt;
ull k1, k2;
ull xorShift128Plus() {
    ull k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= (k3 << 23);
    k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
    return k2 + k4;
}
void gen(int n, int m, int threshold, ull _k1, ull _k2) {
     k1 = _k1, k2 =_k2;
     for (int i = 1; i <= (1 << n); i++) a[i] = xorShift128Plus() % threshold + 1;
     for (int i = 1; i <= m; i++) q[i] = xorShift128Plus() % (n + 1);
}
int ls[maxn], t[maxn];
bool now[25]; ll rm[21], las, rl[23];
void solve(int l, int r, int dep, bool op){
    if(l >= r)return;
    int mid = (l + r) >> 1;
    solve(l, mid, dep - 1, op);
    solve(mid + 1, r, dep - 1, op);
    int p1 = l, pos = l - 1;
    for(int p2 = mid + 1; p2 <= r; ++p2){
        while(p1 <= mid && ls[p1] <= ls[p2]){t[++pos] = ls[p1]; ++p1;}
        if(op)rl[dep] += mid - p1 + 1; else rm[dep] += mid - p1 + 1;
        t[++pos] = ls[p2];
    }
    while(p1 <= mid)t[++pos] = ls[p1++];
    for(int i = l; i <= r; ++i)ls[i] = t[i];
}
int main(){
    cin >> n >> m >> threshold >> k1 >> k2;
    gen(n, m, threshold, k1, k2);
    if(n == 0){
        printf("0\n");
        return 0;
    }
    int mx = (1 << n); 
    for(int i = 1; i <= mx; ++i)ls[i] = a[i];
    sort(ls + 1, ls + mx + 1);
    cnt = unique(ls + 1, ls + mx + 1) - ls - 1;
    for(int i = 1; i <= mx; ++i)a[i] = lower_bound(ls + 1, ls + cnt + 1, a[i]) - ls;
    for(int i = 1; i <= mx; ++i)ls[i] = a[i];
    solve(1, mx, n, 1);
    for(int i = 1; i <= mx; ++i)ls[i] = a[mx - i + 1];
    solve(1, mx, n, 0);
    ll ans = 0;
    for(int i = 1; i <= m; ++i){
        ll las = 0;
        now[q[i]] = 1 - now[q[i]];
        int fl = 1;
        for(int i = n; i >= 1; --i){
            if(now[i])fl = 1 - fl;
            if(!fl)las += rm[i];
            else las += rl[i];
        }
        ans = ans xor ( las * i);
    }
    printf("%lld\n",ans);
    return 0;
}

选择

询问离线,删边变加边,倒着考虑,每条非树边会更新两个点路径,查询其实是查两点之间路径是否全部标记过

可以树剖维护,还可以LCT(我不会)

不过我用了一个奇妙做法

不难发现两点之间路径全被标记过这个东西可以用并查集维护,而并到一个集合上之后树的具体形态已经不重要了

所以可以用类似并查集路径压缩的方法,每次把ulcav路径上遍历的点父亲都改成lca,在并查集中合并即可

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
const int maxn = 500005;
struct edge{
    int u,v;
    edge(){}
    edge(int x, int y){
        u = min(x ,y);
        v = max(x, y);
    }
    friend bool operator < (const edge x, const edge y){return x.u == y.u ? x.v < y.v : x.u < y.u;}
};
struct Edge{int net,to;}e[maxn << 1 | 1];
struct opt{int op,u,v,ans;}o[maxn];
struct SET{
    int f[maxn];
    void pre(int x){
        for(int i = 1; i <= x; ++i)f[i] = i;
    }
    int fa(int x){return f[x] = f[x] == x ? x : fa(f[x]);}
    bool hb(int x, int y){
        x = fa(x); y = fa(y);
        if(x != y)f[x] = y;
        else return false;
        return true;
    }
}S1, S2;
char c[3];
int n, m, q, head[maxn], tot = 1;
void add(int u, int v){
    e[++tot].net = head[u];
    head[u] = tot;
    e[tot].to = v;
}
void link(int u, int v){
    if(S1.hb(u, v) == 0)return;
    add(u, v);add(v, u);
}
int dep[maxn],fa[maxn];
void dfs(int x){
    for(int i = head[x]; i; i = e[i].net){
        int v = e[i].to;
        if(dep[v])continue;
        dep[v] = dep[x] + 1;
        fa[v] = x;
        dfs(v);
    }
}
int LCA(int u, int v){
    while(fa[u] != fa[v]){
        if(dep[fa[u]] > dep[fa[v]])u = fa[u];
        else v = fa[v];
    }
    return u == v ? u : fa[u];
}
void modify(int u, int v){
    int lca = LCA(u, v);
    if(lca == 0)return;
    while(u != lca && u){
        S2.hb(u, lca);
        int lx = u; 
        u = fa[u];
        fa[lx] = lca; 
    }
    u = v;
    while(u != lca && u){
        S2.hb(u, lca);
        int lx = u; 
        u = fa[u];
        fa[lx] = lca; 
    }
}
set<edge>s;
int main(){
    scanf("%d%d%d", &n, &m, &q);
    for(int i = 1; i <= m; ++i){int u ,v; scanf("%d%d",&u, &v); s.insert(edge(u,v));}
    for(int i = 1; i <= q; ++i){
        scanf("%s",c); scanf("%d%d", &o[i].u,&o[i].v);
        if(c[0] == 'Z')o[i].op = 1,s.erase(edge(o[i].u, o[i].v));
        else o[i].op = 0;
    }
    S1.pre(n);
    for(auto x : s) link(x.u, x.v);
    for(int i = q; i >= 1; --i)if(o[i].op) link(o[i].u, o[i].v);
    for(int i = 1; i <= n; ++i)if(!dep[i]) dep[i] = 1,dfs(i);
    S1.pre(n); S2.pre(n);
    for(auto x : s)if(S1.hb(x.u, x.v) == 0)modify(x.u, x.v);
    for(int i = q; i >= 1; --i)
        if(o[i].op){if(S1.hb(o[i].u, o[i].v))continue;modify(o[i].u,o[i].v);}
        else o[i].ans = S2.fa(o[i].u) == S2.fa(o[i].v) ? 1 : 0;
    for(int i = 1; i <= q; ++i)if(!o[i].op){if(o[i].ans)printf("Yes\n");else printf("No\n");}
    return 0;
}
posted @   Chen_jr  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示