八月刷题记录

19 篇文章 0 订阅
9 篇文章 0 订阅

BZOJ3522. [Poi2014]Hotel

给一棵树,问两两之间距离相等的点对有多少对。
点数为5000。
树形dp+统计满足条件的三元组(用差分来统计,即建立两个数组,表示一元组数目、二元组数目)
枚举根节点,对每个子树跑一边dfs统计距离为x的点数。
时间复杂度 O ( n 2 ) O(n^2) O(n2)

//
// Created by artist on 2021/8/5.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

//-----------------------------------------------------------------IO Template
namespace StandardIO {
    template<typename T>
    inline void read(T &x) {
        x = 0;
        T f = 1;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0';
        x *= f;
    }

    template<typename T>
    inline void write(T x) {
        if (x < 0) putchar('-'), x *= -1;
        if (x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace StandardIO;
//-----------------------------------------------------------------IO Template
const int maxn = 5004;
vector<int> G[maxn];
ll ans;
int dp[maxn];
ll t1[maxn],t2[maxn];

void dfs(int u,int fa,int dep){
    dp[dep]++;
    for(int v:G[u]){
        if(v==fa) continue;
        dfs(v,u,dep+1);
    }
}

signed main() {
    int n;read(n);
    for(int i=1,u,v;i<=n-1;++i) {
        read(u),read(v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=n;++i){
        // 枚举中转点
        memset(t1,0,sizeof(t1));
        memset(t2,0,sizeof(t2));
        for(int v:G[i]){
            dfs(v,i,1);
            for(int j=1;j<=n;++j){
                ans += t2[j]*dp[j];
                t2[j] += t1[j]*dp[j];
                t1[j] += dp[j];
                dp[j] = 0;
            }
        }
    }
    write(ans);
}

BZOJ4543. [POI2014]Hotel加强版

数据改成了1e5, O ( n 2 ) O(n^2) O(n2)不可过。
首先考虑将做法改成换根dp。
f [ i ] [ j ] f[i][j] f[i][j] 表示 i i i 的子树中,与 i i i 距离为 j j j 的点数。
g [ i ] [ j ] g[i][j] g[i][j] 表示 i i i 的子树中,有多少个二元点对,满足:再取一个在 i i i 的子树外与 i i i 距离为 j j j 的点即形成三元组的数目。(跟上解的差分思想类似(据说这叫dsu on tree?))
从每棵子树向上贡献。
对每个 u u u ,考虑每个儿子对其的答案贡献:
a n s + = ∑ v ∈ s o n ( u ) g [ v ] [ i + 1 ] ∗ f [ u ] [ i ] + f [ v ] [ i − 1 ] ∗ g [ u ] [ i ] ans += \sum_{v\in son(u)}g[v][i+1]*f[u][i]+f[v][i-1]*g[u][i] ans+=vson(u)g[v][i+1]f[u][i]+f[v][i1]g[u][i]
意义为:在该儿子中找两个点与该儿子外找1个点+在该儿子中找1个点与该儿子外找两个点。
考虑更新:(dsu on tree)
g [ u ] [ i ] + = f [ v ] [ i − 1 ] ∗ f [ u ] [ i ] g[u][i]+=f[v][i-1]*f[u][i] g[u][i]+=f[v][i1]f[u][i]
f [ u ] [ i ] + = f [ v ] [ i − 1 ] f[u][i]+=f[v][i-1] f[u][i]+=f[v][i1]
g [ u ] [ i ] + = g [ v ] [ i + 1 ] g[u][i]+=g[v][i+1] g[u][i]+=g[v][i+1]
显然因为我们要枚举长度,这个做法看起来还是 O ( n 2 ) O(n^2) O(n2)的。
但是考虑进行长链剖分,我们第一次更新时可以更新重子节点,这个步骤利用指针数组进行 O ( 1 ) O(1) O(1)继承重子节点的 g g g f f f,然后再暴力合并其他的轻子节点。
考虑复杂度:重链都是 O ( 1 ) O(1) O(1)继承,每条轻链只被暴力一次。总共时间复杂度: O ( n ) O(n) O(n)

长链剖分:

应用:

  • O(n)统计每个点子树中可合并的以深度为下标的信息
  • 经过一些预处理,单次O(1)在线查询一个点的k级祖先
//
// Created by artist on 2021/8/5.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

//-----------------------------------------------------------------IO Template
namespace StandardIO {
    template<typename T>
    inline void read(T &x) {
        x = 0;
        T f = 1;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0';
        x *= f;
    }

    template<typename T>
    inline void write(T x) {
        if (x < 0) putchar('-'), x *= -1;
        if (x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace StandardIO;
//-----------------------------------------------------------------IO Template

const int maxn = 100005;
vector<int> G[maxn];
ll ans;
ll mem[maxn*10];
ll *f[maxn],*g[maxn]; // dp数组(指针数组)
ll *now=mem+maxn; // 内存位置
int n;
int hei[maxn],sn[maxn];

void pf(int u,int fa){
    for(int v:G[u]){
        if(v==fa) continue;
        pf(v,u);
        if(hei[v]>hei[sn[u]]) sn[u]=v;
    }
    hei[u] = hei[sn[u]] + 1;
}

// 新建节点,赋予内存(每一条长链公用一个内存)
void newnode(int u){
    f[u]=now; now = now+2*hei[u]+1;
    g[u]=now; now = now+2*hei[u]+1;
}

// 按照dfn枚举
void solve(int u,int fa){
    f[u][0] = 1;
    if(sn[u]) {
        f[sn[u]] = f[u] + 1;
        g[sn[u]] = g[u] - 1;
        solve(sn[u],u); // 加上了长链的贡献
        ans += g[sn[u]][1]; // 加上父亲给儿子的三元组贡献
    }

    for(int v:G[u]){
        if(v==fa||v==sn[u]) continue;
        newnode(v);
        solve(v,u);
        for(int i=hei[v];~i;--i){ // 更新
            if(i) ans += f[u][i-1]*g[v][i];
            ans += g[u][i+1]*f[v][i];
            g[u][i+1] += f[u][i+1]*f[v][i];
        }
        for(int i=hei[v];~i;--i){
            f[u][i+1] += f[v][i];
            if(i) g[u][i-1] += g[v][i];
        }
    }
}

signed main() {
    read(n);
    for(int i=1,u,v;i<=n-1;++i) {
        read(u),read(v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    pf(1,0); // 树剖
    newnode(1);
    solve(1,0);
    write(ans);
}

洛谷3233 [HNOI2014]世界树

细节繁琐的虚树+树形dp。
题意:一颗3e5的树,3e5轮,每一轮给出m个关键点(m的总和不超过3e5),求距离每个关键点最近的点的个数。
题给数据和形式一眼就能看出是虚树,但是dp很难弄,细节很多。
参考题解。
首先对原树进行基本处理,把子树大小、lca、深度、dfn序整出来。(1)
然后用dfn序建虚树。
首先处理出虚树上的点的最近关键点是谁,距离为多少。
这需要一次自下而上的dfs处理每个虚树点的子树中最近关键点,又需要一次自上而下的dfs算进去子树外的最近关键点进行比较。(21和22)
然后我们统计虚树上没有的点对关键点的贡献。
首先,我们贡献掉那些位于叶子的点。虚树上一个点u,若其有一个子树,都没有虚树点,那么这个子树的最近关键点肯定跟u共用关键点。于是我们考虑对每个u,都先贡献给其关键点sz[u]。然后遍历u的子树,利用lca找到该子树在原树中u的儿子,其关键点的答案减去这个儿子为根的子树的sz。(3)
现在我们考虑贡献掉那些位于虚树边上的点。这些点在原树中,却不在虚树中。对于一个u,若其儿子(虚树上的)v和u所属关键点不同。那么(u,v)上一定存在一个深度,位于这个深度的点的子树(直到v)都属于v的关键点,(u,v)上其他节点就属于u的关键点。那么我们先算出这个深度,然后使用倍增到达那个点。再进行贡献。(4)

//
// Created by Artist on 2021/8/5.
//

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
#define pii pair<int,int>
#define mkp make_pair
#define dis first
#define ver second
#define pb push_back
#define DB(args...) do {cout<<#args<<" : ";dbg(args);}while(0)
void dbg() {std::cout<<" #\n";}
template<typename T, typename...Args>
void dbg(T a,Args...args) {std::cout<<a<<" ";dbg(args...);}
vector<int> G[maxn]; // ori
vector<int> g[maxn]; // vir
const int LOG = __lg(maxn)+1;
int f[maxn][LOG+3],dep[maxn],dfn[maxn],up[maxn],crt[maxn],sz[maxn]; // parent & depth
int n,m,rt,cnt,top;
int ans[maxn];
int st[maxn]; // 要手写栈
pii pt[maxn]; // 距离最近的距离及 critical point编号

void dfs1(int u){
    sz[u]=1;
    dfn[u]=++cnt;
    for(int v:G[u]) if(v!=f[u][0]) {
            dep[v]=dep[u]+1;
            f[v][0]=u;
            dfs1(v);
            sz[u]+=sz[v];
        }
}

void dfs21(int u){
    if(crt[u]) pt[u]=mkp(0,u);
    else pt[u]=mkp(1000000,0);
    for(int v:g[u]){
        dfs21(v);
        pii tmp=mkp(pt[v].dis+dep[v]-dep[u],pt[v].ver);
        pt[u]=min(pt[u],tmp);
    }
}

void dfs22(int u){
    for(int v:g[u]) {
        if(!crt[v]) pt[v]=min(pt[v],mkp(pt[u].dis+dep[v]-dep[u],pt[u].ver));
        dfs22(v);
    }
}

void dfs3(int u){
    ans[pt[u].ver] += sz[u];
    for(int v:g[u]){
        int x=v;
        for(int j=LOG;~j;--j) if(f[x][j] && dep[f[x][j]]>dep[u]) x=f[x][j];
        ans[pt[u].ver] -= sz[up[v]=x];
        dfs3(v);
    }
}


void dfs4(int u){
    for(int v:g[u]){
        if (pt[u].ver==pt[v].ver) ans[pt[u].ver] += sz[up[v]] - sz[v];
        else {
            // 倍增
            int x=up[v],y=v,h;
            h = dep[pt[v].ver] + dep[u] - pt[u].dis;
            h=h&1?h+1>>1:(pt[v].ver<pt[u].ver?h>>1:(h>>1)+1);
            for(int j=LOG;~j;--j) if(f[y][j] && dep[f[y][j]] >= h) y=f[y][j];
            ans[pt[v].ver] += sz[y] - sz[v];
            ans[pt[u].ver] += sz[x] - sz[y];
        }
        dfs4(v);
    }
}

void dfs5(int u){
    crt[u]=0;
    ans[u]=0;
    for(int v:g[u]){
        dfs5(v);
    }
    g[u].clear();
}

int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int j=LOG;~j;--j)
        if(dep[f[x][j]]>=dep[y] && f[x][j]) x=f[x][j];
    if(x==y) return x;
    for(int j=LOG;~j;--j)
        if(f[x][j]!=f[y][j] && f[x][j])
            x=f[x][j],y=f[y][j];
    return f[x][0];
}

int a[maxn],ori[maxn];
bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}

int main(){
    scanf("%d",&n);
    for(int i=1,x,y;i<=n-1;++i){
        scanf("%d%d",&x,&y);
        G[x].pb(y),G[y].pb(x);
    }
    // 预处理
    dfs1(1);

    // lca
    for(int j=1;j<=LOG;++j) for(int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];

    int q;scanf("%d",&q);
    while(q--){
        scanf("%d",&m);
        for(int i=1;i<=m;++i) {
            scanf("%d",&a[i]);
            ori[i]=a[i];
            crt[a[i]]=1; // is critical point
        }

        // virtual tree
        sort(a+1,a+1+m,cmp);
        top = 0;
        st[++top]=a[1];
        for(int i=2;i<=m;++i){
            int x=a[i],y=lca(x,st[top]);
            while(top>1 && dfn[y] <= dfn[st[top-1]])
                g[st[top-1]].pb(st[top]),top--;
            if(st[top]!=y) g[y].pb(st[top]),st[top]=y;
            st[++top] = x;
        }
        while(top>1) g[st[top-1]].pb(st[top]),top--;
        rt=st[1];

        // 求出每个虚树节点靠得最近的"关键节点"
        dfs21(rt);
        dfs22(rt);

        // 贡献上没有关键节点的子树
        dfs3(rt);
        // 贡献上道路中间的点
        dfs4(rt);
        ans[pt[rt].ver] += sz[1] - sz[rt];

        for(int i=1;i<=m;++i)
            printf("%d%c",ans[ori[i]],i==m?'\n':' ');

        // 清空
        dfs5(rt);
    }
}

2021牛客多校第八场 D-OR

一个性质: a + b − a ∧ b = a ∨ b a+b - a\wedge b=a\vee b a+bab=ab
bitmask计数

//
// Created by artist on 2021/8/9.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

//-----------------------------------------------------------------IO Template
namespace StandardIO {
    template<typename T>
    inline void read(T &x) {
        x = 0;
        T f = 1;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0';
        x *= f;
    }

    template<typename T>
    inline void write(T x) {
        if (x < 0) putchar('-'), x *= -1;
        if (x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace StandardIO;
//-----------------------------------------------------------------IO Template
const int maxn = 1e5+4;
int b[maxn],c[maxn],num[maxn]; // 暂时存
int cnt,lst;
// 上一位的可能情况数,以及上一位的可能情况数如果是1的话,是谁

signed main() {
    int n;read(n);
    for(int i=2;i<=n;++i){
        read(b[i]);
    }
    for(int i=2;i<=n;++i){
        read(c[i]);
        c[i] = c[i]-b[i];
    }
    ll ans = 1;
    for(int i=0;i<31;++i){
        int flg=2;
        num[1]=1;
        for(int j=2;j<=n;++j){
            int cc = (c[j]>>i)&1;
            int bb = (b[j]>>i)&1;
            if(cc==1){
                num[j]=1;
            }else{
                num[j]=bb==1?1-num[j-1]:0;
            }
            if((num[j-1]|num[j])!=bb||(num[j-1]&num[j])!=cc) {
                flg--;
                break;
            }
        }
        num[1]=0;
        for(int j=2;j<=n;++j){
            int cc = (c[j]>>i)&1;
            int bb = (b[j]>>i)&1;
            if(cc==1){
                num[j]=1;
            }else{
                num[j]=bb==1?1-num[j-1]:0;
            }
            if((num[j-1]|num[j])!=bb||(num[j-1]&num[j])!=cc) {
                flg--;
                break;
            }
        }
        ans *= flg;
        if(ans==0) break;
    }
    write(ans);
}

2021牛客多校第八场 F-Robots

离线处理+bitset优化

//
// Created by artist on 2021/8/9.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define pb push_back
//-----------------------------------------------------------------IO Template
namespace StandardIO {
    template<typename T>
    inline void read(T &x) {
        x = 0;
        T f = 1;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0';
        x *= f;
    }

    template<typename T>
    inline void write(T x) {
        if (x < 0) putchar('-'), x *= -1;
        if (x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace StandardIO;
//-----------------------------------------------------------------IO Template
const int maxn = 503;
const int maxq = 5e5+5;
char mp[maxn][maxn];
struct node{int x,y,id,opt;};
vector<node> rbt[maxn][maxn];
bitset<maxq> ans;
bitset<260004> bs[maxn]; // 每个y坐标,给一个可达图,开了250003过不了,不知道为什么

signed main() {
    int n,m;read(n),read(m);
    for(int i=1;i<=n;++i){
        scanf("%s",mp[i]+1);
    }
    int q;read(q);
    for(int i=1,t,x1,y1,x2,y2;i<=q;++i){
        read(t),read(x1),read(y1),read(x2),read(y2);
        rbt[x1][y1].pb(node{x2,y2,i,t});
    }
    for(int i=n;i;--i){
        for(int j=m;j;--j){
            if (mp[i][j]=='0'){
                bs[j]|=bs[j+1]; // 继承右边的。继承下面这个步骤已经自动处理了(有点类似滚动数组),因为是个dp从下一行for到这一行
                bs[j][m*i+j]=true; // 可达自己当前这个位置
            }else{
                bs[j].reset(); // 当前不可达,就封死
            }
            for(auto rb:rbt[i][j]){ // 对起点在当前点的所有robot处理答案
                if(rb.opt==1){
                    ans[rb.id]=(rb.y==j && bs[j][m*rb.x+rb.y]);
                }else if(rb.opt==2){
                    ans[rb.id]=(rb.x==i && bs[j][m*rb.x+rb.y]);
                }else{
                    ans[rb.id]=bs[j][m*rb.x+rb.y];
                }
            }
        }
    }
    for(int i=1;i<=q;++i) printf(ans[i]?"yes\n":"no\n");
}

2021牛客多校第八场 J-Tree

博弈+树+rmq

//
// Created by artist on 2021/8/10.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
void dbg() { std::cout << "  #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

const int maxn = 1e6+5;
vector<int> G[maxn];
int onpath[maxn]; // 在s-t路径上
int hei[maxn];
int parent[maxn]; // 路径
vector<int> d;
int st[3][maxn][23];
const int inf = 0x3f3f3f3f;

void dfs(int u,int fa){
    hei[u]=0;
    for(int v:G[u]){
        if(v==fa || onpath[v]) continue;
        parent[v]=u;
        dfs(v,u);
        hei[u]=max(hei[v]+1,hei[u]);
    }
}

int Query(int l,int r,int opt){
    int k=__lg(r-l+1);
    return max(st[opt][l][k],st[opt][r-(1<<k)+1][k]);//把拆出来的区间分别取最值
}

int solve(int l,int r,int opt){
    // 先手位置,后手位置,现在是谁
    if(l>=r) return opt?-inf:inf;
    if(!opt){
        return max(solve(l+1,r,1),st[0][l][0]-Query(l+1,r,1));
    }else{
        return min(solve(l,r-1,0),Query(l,r-1,0)-st[1][r][0]);
    }
}

signed main() {
    int n,s,t;scanf("%d%d%d",&n,&s,&t);
    for(int i=1,u,v;i<n;++i){
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    parent[s]=0;
    dfs(s,0);
    // 标记路上的节点
    for(int f=t;f;f=parent[f]){
        onpath[f]=1;
    }
    // 一路回溯,记录路径外的高度
    for(int f=t;f;f=parent[f]){
        dfs(f,0);
        d.push_back(hei[f]);
    }
    reverse(d.begin(),d.end());
    int m=d.size();
    for(int i=0;i<m;++i){
        st[0][i+1][0]=d[i]+i;
        st[1][i+1][0]=d[i]+m-1-i;
    }
    // rmq
    for(int j=1;(1<<j)<=m;j++){
        for(int i=1;i+(1<<(j-1))<=m;i++){
            st[0][i][j]=max(st[0][i][j-1],st[0][i+(1<<(j-1))][j-1]);
            st[1][i][j]=max(st[1][i][j-1],st[1][i+(1<<(j-1))][j-1]);
        }
    }
    printf("%d\n",solve(1,m,0));
}

LOJ105 文艺平衡树

用来学splay
1.无旋treap版本

//
// Created by artist on 2021/6/17.
//

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn = 1e5+5;

struct node{
    int key,lch,rch,pri,sz,lzy;
    node(int k=0,int l=0,int r=0,int p=0,int s=0):key(k),lch(l),rch(r),pri(p),sz(s){}
}tr[maxn<<2];

int cnt,rt;

#define key(x) tr[x].key
#define pri(x) tr[x].pri
#define lch(x) tr[x].lch
#define rch(x) tr[x].rch
#define sz(x) tr[x].sz
#define lzy(x) tr[x].lzy

int newnode(int v){
    tr[++cnt]=node(v,0,0,rand(),1);
    return cnt;
}

void push_up(int u){
    sz(u)=sz(lch(u))+sz(rch(u))+1;
}

void push_down(int u){
    if(lzy(u)){
        lzy(u)=0;
        swap(lch(u),rch(u));
        lzy(lch(u))^=1,lzy(rch(u))^=1;
    }
}

// 把区间分为中序遍历序号小于等于k的和大于k的
pair<int,int> split(int u,int k){
    if(!u) return make_pair(0,0);
    push_down(u);
    if(k<=sz(lch(u))){
        pair<int,int> o = split(lch(u),k);
        lch(u)=o.second;
        push_up(u);
        return make_pair(o.first,u);
    }else{
        pair<int,int> o = split(rch(u),k-sz(lch(u))-1);
        rch(u)=o.first;
        push_up(u);
        return make_pair(u,o.second);
    }
}

// 满足u的所有点key小于v所有点的key
int merge(int u,int v){
    // u==0或v==0
    if(!u||!v) return u+v;
    push_down(u);
    push_down(v);
    if(pri(u)<pri(v)){
        rch(u)=merge(rch(u),v);
        push_up(u);
        return u;
    }else{
        lch(v)=merge(u,lch(v));
        push_up(v);
        return v;
    }
}

// 这个l和r是区间..不是key...
void rev(int l,int r){
    pair<int,int> o = split(rt,l-1);
    pair<int,int> q = split(o.second,r-l+1);
    lzy(q.first)^=1;
    rt=merge(o.first,merge(q.first,q.second));
}

void dfs(int u){
    if(!u) return;
    push_down(u);
    dfs(lch(u));
    printf("%d ",u);
    dfs(rch(u));
}

int main(){
    srand(time(NULL));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) rt=merge(rt,newnode(i));
    for(int i=1,l,r;i<=m;++i){
        scanf("%d%d",&l,&r);
        rev(l,r);
    }
    dfs(rt);
}

2.splay版本

这里的Splay维护的显然不再是权值排序
现在按照的是序列中的编号排序(不过在这道题目里面就是权值诶。。。)
那么,继续考虑,其实最终的结果也就是整颗Splay的中序遍历(平衡树的性质诶)
那么,现在如果按照权值来维护显然是不正确的
继续找找规律,发现,如果一个点在序列中的位置为第K个
那么,他就是平衡树的第K大(就当做普通的Splay来看的话)
所以,序列中的位置就变成了区间的第K大点
继续考虑如何翻转
翻转也就是整颗子树的每一个节点的左右儿子交换
因此,只要在根节点的地方打一个标记
在旋转之前下方一下标记就行了
最后输出的时候输出的就是Splay的中序遍历
至于初始的Splay怎么建立,可以直接构造完美的Splay
像我这种比较懒得,直接弄了一个insert。。。
那么我们现在已经有一棵编号树了(并且由于递归建树,一开始是平衡的),我们要对它进行区间翻转操作。那么实际上我们可以发现,在反转区间[l,r]的时候,我们可以考虑利用Splay的性质,将l-1翻转至根节点,再将r+1翻转至根节点的幼儿子

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+5;
int fa[maxn],sz[maxn],cnt[maxn],ch[maxn][2],val[maxn],rt,tot,flg[maxn];
int n;
// 0:左儿子,1:右儿子
struct Splay{
    void maintain(int x) {sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];}
    bool get(int x) {return x==ch[fa[x]][1];}
    void clear(int x) {
        ch[x][0]=ch[x][1]=fa[x]=val[x]=sz[x]=cnt[x]=0;
    }
    void rotate(int x){
        int y=fa[x],z=fa[y],chk=get(x);
        ch[y][chk]=ch[x][chk^1];
        if(ch[x][chk^1]) fa[ch[x][chk^1]]=y;
        ch[x][chk^1]=y;
        fa[y]=x;
        fa[x]=z;
        if(z) ch[z][y==ch[z][1]] = x;
        maintain(x);
        maintain(y);
    }
    void push_down(int x) {
        if(flg[x]) {
            flg[x]=0;
            swap(ch[x][0],ch[x][1]);
            flg[ch[x][0]]^=1;
            flg[ch[x][1]]^=1;
        }
    }
    void splay(int x,int goal){
        for(int f;f=fa[x],f!=goal;rotate(x))
            if(fa[f]!=goal) rotate(get(x)==get(f)?f:x);
        if(!goal) rt=x;
    }
    void ins(int k){
        if(!rt){
            val[++tot]=k;
            cnt[tot]++;
            rt=tot;
            maintain(rt);
            return;
        }
        int cur = rt, f = 0;
        while(1){
            if(val[cur]==k){
                cnt[cur]++;
                maintain(cur);
                maintain(f);
                splay(cur,0);
                break;
            }
            f=cur;
            cur=ch[cur][val[cur]<k];
            if(!cur){
                val[++tot]=k;
                cnt[tot]++;
                fa[tot]=f;
                ch[f][val[f]<k]=tot;
                maintain(tot);
                maintain(f);
                splay(tot,0);
                break;
            }
        }
    }
    int kth(int k){
        int cur=rt;
        while(1){
            push_down(cur);
            if(ch[cur][0]&&k<=sz[ch[cur][0]]){
                cur=ch[cur][0];
            }else{
                k-=cnt[cur]+sz[ch[cur][0]];
                if(k<=0){
                   // splay(cur,0);
                    return val[cur];
                }
                cur=ch[cur][1];
            }
        }
    }
    void rev(int l,int r){
        int ll=l-1?kth(l-1):0,rr=r==n?0:kth(r+1);
        if(ll) splay(ll,0);
        if(rr) splay(rr,ll);
        int cur;
        if(ll&&rr) cur=ch[rr][0];  // 防止如果旋转的是边界
        else if(ll) cur=ch[rt][1];
        else if(rr) cur=ch[rr][0];
        else cur=rt;
        flg[cur]^=1;
    }
    void dfs(int u){
        if(!u) return;
        push_down(u);
        dfs(ch[u][0]);
        printf("%d ",u);
        dfs(ch[u][1]);
    }
}tr;
int main(){
    freopen("in.in","r",stdin);
    int m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        tr.ins(i);
    }
    for(int i=1,l,r;i<=m;++i){
        scanf("%d%d",&l,&r);
        tr.rev(l,r);
    }
    tr.dfs(rt);
}

BZOJ3282 Tree(lct模板)

求一条路径的异或和。
操作包括:加边、删边、改点权

//
// Created by artist on 2021/8/11.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int n;
const int maxn = 3e5+5;
int flg[maxn],val[maxn],f[maxn],ch[maxn][2],sum[maxn];

struct lct{
#define Get(x) (ch[f[x]][1]==x)
#define isRoot(x) (ch[f[x]][0]!=x && ch[f[x]][1]!=x)
    inline void push_up(int x){
        sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];
    }
    inline void push_down(int x){
        if(flg[x]){
            flg[x]=0;
            swap(ch[x][0],ch[x][1]);
            flg[ch[x][0]]^=1;
            flg[ch[x][1]]^=1;
        }
    }
    inline void rotate(int x){
        int y=f[x],z=f[y],k=Get(x);
        if(!isRoot(y)) ch[z][ch[z][1]==y]=x;
        f[ch[y][k]=ch[x][!k]]=y;
        ch[x][!k]=y,f[y]=x,f[x]=z;
        push_up(y),push_up(x);
    }
    inline void splay(int x){
        update(x);
        for(int fa;fa=f[x],!isRoot(x);rotate(x))
            if(!isRoot(fa)) rotate(Get(fa)==Get(x)?fa:x);
    }
    inline int access(int x){
        int p;
        for(p=0;x;p=x,x=f[x])
            splay(x),ch[x][1]=p,push_up(x);
        return p;
    }
    inline void update(int p){
        if (!isRoot(p)) update(f[p]);
        push_down(p);
    }
    inline void makeRoot(int p){
        p=access(p);
        flg[p]^=1;
        push_down(p);
    }
    inline void link(int x,int p){
        // 先判断是否合法
        if(Find(x)==Find(p)) return;
        makeRoot(x);
        splay(x);
        f[x]=p;
    }
    inline void split(int x,int y){
        makeRoot(x);
        access(y);
        splay(y);
    }
    inline void cut(int x,int p){
        // 先判断是否合法
        // 1.在一个连通块内 2.中序遍历紧挨着
        if(Find(x)!=Find(p)) return;
        makeRoot(x),access(p),splay(p);
        if(ch[p][0]!=x) return;
        ch[p][0]=f[x]=0;
    }
    inline int Find(int p){
        access(p),splay(p),push_down(p);
        while(ch[p][0]) p=ch[p][0],push_down(p);
        splay(p);
        return p;
    }
}tr;

signed main() {
    int m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&val[i]);
    for(int i=1;i<=m;++i){
        int opt,x,y;scanf("%d%d%d",&opt,&x,&y);
        if(opt==0){
            tr.split(x,y);
            printf("%d\n",sum[y]);
        }else if(opt==1){
            tr.link(x,y);
        }else if(opt==2){
            tr.cut(x,y);
        }else{
            tr.access(x);
            tr.splay(x);
            val[x]=y;
            tr.push_up(x);
        }
    }
}

洛谷P3203 [HNOI2010]弹飞绵羊

lct裸题
新建一个点表示被弹飞

//
// Created by artist on 2021/8/11.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}
const int maxn = 2e5+5;

int flg[maxn],f[maxn],ch[maxn][2],sum[maxn];

struct lct{
#define Get(x) (ch[f[x]][1]==x)
#define isRoot(x) (ch[f[x]][0]!=x && ch[f[x]][1]!=x)
    inline void push_up(int x){
        sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+1;
    }
    inline void push_down(int x){
        if(flg[x]){
            flg[x]=0;
            swap(ch[x][0],ch[x][1]);
            flg[ch[x][0]]^=1;
            flg[ch[x][1]]^=1;
        }
    }
    inline void rotate(int x){
        int y=f[x],z=f[y],k=Get(x);
        if(!isRoot(y)) ch[z][ch[z][1]==y]=x;
        f[ch[y][k]=ch[x][!k]]=y;
        ch[x][!k]=y,f[y]=x,f[x]=z;
        push_up(y),push_up(x);
    }
    inline void splay(int x){
        update(x);
        for(int fa;fa=f[x],!isRoot(x);rotate(x))
            if(!isRoot(fa)) rotate(Get(fa)==Get(x)?fa:x);
    }
    inline int access(int x){
        int p;
        for(p=0;x;p=x,x=f[x])
            splay(x),ch[x][1]=p,push_up(x);
        return p;
    }
    inline void update(int p){
        if (!isRoot(p)) update(f[p]);
        push_down(p);
    }
    inline void makeRoot(int p){
        p=access(p);
        flg[p]^=1;
        push_down(p);
    }
    inline void link(int x,int p){
        // 先判断是否合法
        if(Find(x)==Find(p)) return;
        makeRoot(x);
        splay(x);
        f[x]=p;
    }
    inline void split(int x,int y){
        makeRoot(x);
        access(y);
        splay(y);
    }
    inline void cut(int x,int p){
        // 先判断是否合法
        // 1.在一个连通块内 2.中序遍历紧挨着
        if(Find(x)!=Find(p)) return;
        makeRoot(x),access(p),splay(p);
        if(ch[p][0]!=x) return;
        ch[p][0]=f[x]=0;
    }
    inline int Find(int p){
        access(p),splay(p),push_down(p);
        while(ch[p][0]) p=ch[p][0],push_down(p);
        splay(p);
        return p;
    }
}tr;

int a[maxn];

signed main() {
    int n;scanf("%d",&n);
    for(int i=1,y;i<=n;++i){
        scanf("%d",&y);
        a[i]=y;
        if(i+y>n) tr.link(i,n+1);
        else tr.link(i,i+y);
    }
    int m;scanf("%d",&m);
    for(int i=1;i<=m;++i){
        int opt,x,y;scanf("%d%d",&opt,&x);
        x++;
        if(opt==1){
            tr.makeRoot(x);
            tr.access(n+1);
            tr.splay(n+1);
            printf("%d\n",sum[n+1]-1);
        }else{
            scanf("%d",&y);
            if(x+a[x]<=n) tr.cut(x,x+a[x]);
            else tr.cut(x,n+1);
            a[x]=y;
            if(x+a[x]<=n) tr.link(x,x+a[x]);
            else tr.link(x,n+1);
        }
    }
}

牛客第九场 I Incentive Model

题意:A和B竞争,A初始x/y,B初始1-x/y。Ta:Pr[Ta=t]=(1-pA){t-1}pA(几何分布)。赢下一个块的条件:Ta<Tb。赢了一个块:奖励w的得分,得分可以增加下一次挖矿的效率。pa=a/{2256}={k*w+x/y}/{2^256}。问A的期望赢的块数。

X i ∈ 0 , 1 X_i\in {0,1} Xi0,1 分别表示矿工A能不能获得第i个区块。令 S i S_i Si 表示在竞争完第 i i i 个区块后A的stake。

S 0 = a S_0=a S0=a

首先我们考虑 X i X_i Xi为1的概率。(概率论白学现场)= S i − 1 1 + w ( i − 1 ) \frac{S_{i-1}}{1+w(i-1)} 1+w(i1)Si1 (已经发放了i-1次奖励)。(我也不知道为啥直接a之于总和的概率这样就可以了)

显然有 S i + 1 = S i + w X i + 1 S_{i+1}=S_i+wX_{i+1} Si+1=Si+wXi+1

考虑条件期望(俺也不会)。 E [ S i + 1 ∣ S i ] = S i + S i 1 + w i E[S_{i+1}|S_i]=S_i+\frac{S_i}{1+wi} E[Si+1Si]=Si+1+wiSi

那么 E [ S i + 1 ] = E [ E [ S i + 1 ∣ S i ] ] = E [ S i ] × 1 + w i + w 1 + w i E[S_{i+1}]=E[E[S_{i+1}|S_i]]=E[S_i]\times\frac{1+wi+w}{1+wi} E[Si+1]=E[E[Si+1Si]]=E[Si]×1+wi1+wi+w(发现是一个递推过程)

因此有 E [ S i + 1 ] = E [ S 0 ] × ∏ j = 0 i 1 + w j + w 1 + w j = a × ∏ j = 0 i 1 + w ( j + 1 ) 1 + w j E[S_{i+1}]=E[S_0]\times\prod_{j=0}^{i}{\frac{1+wj+w}{1+wj}}=a\times \prod_{j=0}^i\frac{1+w(j+1)}{1+wj} E[Si+1]=E[S0]×j=0i1+wj1+wj+w=a×j=0i1+wj1+w(j+1)

因此 E [ S i ] = a × ( 1 + w i ) E[S_i]=a\times(1+wi) E[Si]=a×(1+wi)

答案就是 n × E [ λ A ] = n a × ( 1 + w n ) − a w n = a n n\times E[\lambda_A]=n\frac{a\times(1+wn)-a}{wn}=an n×E[λA]=nwna×(1+wn)a=an

#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int qpow(int a,int n){
    long long ans=1;
    while(n){
        if(n&1) ans=ans*a%mod;
        a=(long long)a*a%mod;
        n>>=1;
    }
    return ans;
}
int main(){
    int n,w,x,y;cin>>n>>w>>x>>y;
    cout<<(long long)x*qpow(y,mod-2)%mod*n%mod<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值