【数据结构题】关于最近集训vp的几道银铜牌数据结构

1、【F. Strange Memory】

题目来源:

2020 CCPC 长春站 F. Strange Memory

难度:

铜牌🥉+(参照比赛正式队情况)

题意:

给定一棵有根树,根为 1 1 1,对于节点 i i i 有一个权值 a i a_i ai,求 ∑ i = 1 n ∑ j = i + 1 n [ a i ⊕ a j = a l c a ( i , j ) ] ( i ⊕ j ) \sum_{i=1}^n\sum_{j=i+1}^n[a_i\oplus a_j=a_{lca(i,j)}](i\oplus j) i=1nj=i+1n[aiaj=alca(i,j)](ij),其中 l c a ( i , j ) lca(i,j) lca(i,j) 表示节点 i i i j j j 的最近公共祖先。

算法:

树上启发式合并(dsu on tree)

思路:

遍历以节点 k k k 作为点 i i i 的祖先时,暴力枚举与点 i i i 不在同一棵 k k k 的儿子子树的点 j j j(确保了 l c a ( i , j ) = k lca(i,j)=k lca(i,j)=k),满足 a i ⊕ a j = a k a_i\oplus a_j=a_k aiaj=ak
每遍历一棵点 k k k 的儿子子树后,将所遍历的点的映射关系 ( a i → i ) (a_i\rightarrow i) (aii) v e c t o r vector vector 记录,再在遍历下一棵儿子子树时,假设当前位于点 j j j,只需将答案加上 v e c t o r [ a k ⊕ a j ] vector[a_k\oplus a_j] vector[akaj] 中的每一个值异或上 j j j
考虑dsu on tree优化复杂度,对于重儿子的映射关系,父亲可以直接继承。

#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);i++)
#define R(i,j,k) for(ll i=(j);i>=(k);i--)
#define inf 0x3f3f3f3f3f3f3f3f
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}//a 分母; b MOD-2
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};

ll m,n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,num[N],sum[N],mn,mx,ans;
ll lim,pos,tot,cnt,key,block;
ll a[N],b[N];
double dans;
bool vis[N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;

bool cmp(qq u,qq v){
    return u.x>v.x;
}
bool cmp1(qq u,qq v){
    return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
    return u>v;
}};//shun序

vec<ll>sv[N];
struct tree{ll fa,dep,dfn,siz,son,w;}tr[N];
struct edge{
    ll cnt,hed[N],to[N*2],nxt[N*2];
    void add(ll u,ll v){to[++cnt]=v;nxt[cnt]=hed[u];hed[u]=cnt;}
    void ADD(ll u,ll v){add(u,v);add(v,u);}
    void clear(ll n){cnt=0;L(i,1,n)hed[i]=0;}
}eg;

void dfs0(ll u,ll ac){//计算重儿子
    tr[u].fa=ac;
    tr[u].dep=tr[tr[u].fa].dep+1;
    tr[u].siz=1;
    for(ll i=eg.hed[u];i;i=eg.nxt[i]){
        ll v=eg.to[i];
        if(v!=ac){
            dfs0(v,u);
            tr[u].siz+=tr[v].siz;
            if(!tr[u].son||tr[v].siz>tr[tr[u].son].siz)tr[u].son=v;
        }
    }
}

void dfs2(ll u,ll r){//计算一棵子树对答案的贡献
    ll p=a[u]^a[r];
    if(!sv[p].empty()){
        for(auto x:sv[p]){
            ans+=x^u;
        }
    }
    for(ll i=eg.hed[u];i;i=eg.nxt[i]){
        ll v=eg.to[i];
        if(v!=tr[u].fa)dfs2(v,r);
    }
}

void dfs3(ll u){//记录这棵子树的映射关系
    if(sv[a[u]].empty())b[++cnt]=a[u];
    sv[a[u]].pb(u);
    for(ll i=eg.hed[u];i;i=eg.nxt[i]){
        ll v=eg.to[i];
        if(v!=tr[u].fa)dfs3(v);
    }
}

void dfs1(ll u,bool type){//遍历节点u作为lca
    for(ll i=eg.hed[u];i;i=eg.nxt[i]){
        ll v=eg.to[i];
        if(v!=tr[u].fa&&v!=tr[u].son)dfs1(v,1);
    }
    if(tr[u].son)dfs1(tr[u].son,0);
    if(sv[a[u]].empty())b[++cnt]=a[u];
    sv[a[u]].pb(u);
    
    for(ll i=eg.hed[u];i;i=eg.nxt[i]){
        ll v=eg.to[i];
        if(v!=tr[u].fa&&v!=tr[u].son){
            dfs2(v,u);
            dfs3(v);
        }
    }
    if(type){L(i,1,cnt)sv[b[i]].clear();cnt=0;}//type表示是否为轻儿子
}

void solve(){
    scanf("%lld",&n);
    L(i,1,n)scanf("%lld",&a[i]);
    L(i,1,n-1){
        scanf("%lld%lld",&x,&y);
        eg.ADD(x,y);
    }
    ans=0;
    dfs0(1,0);
    dfs1(1,1);
    printf("%lld\n",ans);
}

int main(){
    // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    // cout<<fixed<<setprecision(12);//精度
    ll Case=1;
    //scanf("%lld",&Case);
    while(Case--)solve();
}

2、【K. Ragdoll】

题目来源:

2020 CCPC 长春站 K. Ragdoll

难度:

银牌🥈+(参照比赛正式队情况)

题意:

给定一个 n n n 个节点 0 0 0 条边的无向图(最初每个节点互不连通),对于节点 i i i 有一个权值 a i a_i ai,接下来有 m m m 次操作,每次操作有以下三种:

  1. 新增一个节点 x x x,权值为 v v v
  2. 连接节点 x x x 与节点 y y y
  3. 将节点 x x x的权值改为 v v v

每次操作完后,输出有多少个节点对 ( i , j ) (i,j) (i,j),满足节点 i i i 与节点 j j j 在同一连通块中,且 g c d ( a i , a j ) = a i ⊕ a j gcd(a_i,a_j)=a_i\oplus a_j gcd(ai,aj)=aiaj。(节点对 ( i , j ) (i,j) (i,j) ( j , i ) (j,i) (j,i) 被视作同一个节点对)

算法:

并查集+启发式合并

思路:

预处理所有的 g c d ( x , y ) = x ⊕ y gcd(x,y)=x\oplus y gcd(x,y)=xy
枚举 d = g c d ( x , y ) d=gcd(x,y) d=gcd(x,y),枚举 i i i使得 x = i d x=id x=id,那么 y = x ⊕ d y=x\oplus d y=xd,如果 g c d ( x , y ) = d gcd(x,y)=d gcd(x,y)=d 就记录 ( x , y ) (x,y) (x,y) 对。
预处理复杂度 w l o g w wlogw wlogw
考虑更新答案的操作只有合并或者改值,我们考虑对每一个连通块用一个 s e t set set 维护权值。
对于合并操作,遍历小连通块的 s e t set set,依据预处理的值,去寻找大连通块的 s e t set set 中的值即可。
改值操作同理,考虑删除该值对答案的影响,答案减去该连通块中与旧值形成的对的个数;考虑新增值对答案的影响,答案加上该连通块中与新值形成的对的个数。

#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};

ll m,n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N];
double dans;
bool vis[N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;

bool cmp(qq u,qq v){
    return u.x>v.x;
}
bool cmp1(qq u,qq v){
    return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
    return u>v;
}};//shun序

pair<ll,ll>pr;
vector<ll>sv[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,bool>mp;
set<ll>::iterator it;
bitset<M>bi;

#define sit set<node>::iterator
struct node{
    ll val;
    mutable ll num;
    node(ll l,ll r):val(l),num(r){}
    bool operator <(const node& u)const{
        return val<u.val;
    }
};
set<node>se[N];

void init(ll n){
    L(d,1,n){
        L(i,1,n){
            if(i*d>n)break;
            ll id=i*d,jd=(id)^d;
            if(jd%d==0&&jd>0&&jd<=n){
                ll j=jd/d;
                if(gcd(i,j)==1){
                    cnt++;b[id]++;
                    sv[id].pb(jd);
                }
            }
        }
    }
}

ll cut(ll i,ll x){
    sit it=se[i].lower_bound(node(x,0));
    if(it==se[i].end())return 0;
    if(it->val!=x)return 0;
    return it->num;
}

void del(ll i,ll x){
    sit it=se[i].lower_bound(node(x,0));
    node tmp=*it;
    if(tmp.num==1)se[i].erase(it);
    else it->num-=1;
}

void ins(ll i,ll x,ll cs){
    sit it=se[i].lower_bound(node(x,0));
    if(it!=se[i].end()){
        if(it->val==x)it->num+=cs;
        else se[i].insert(node(x,cs));
    }
    else se[i].insert(node(x,cs));
}

ll pre[N],siz[N];
ll fd(ll x){
    if(x==pre[x])return x;
    else return pre[x]=fd(pre[x]);
}

void meg(ll x,ll y){
    ll fx=fd(x),fy=fd(y);
    if(fx!=fy){
        if(siz[fx]>=siz[fy]){
            for(node it:se[fy]){
                ll is=it.num;
                for(auto ip:sv[it.val]){
                    ll js=cut(fx,ip);
                    ans+=is*js;
                }
            }
            for(node it:se[fy])ins(fx,it.val,it.num);
            pre[fy]=fx;
            siz[fx]+=siz[fy];
        }
        else{
            for(node it:se[fx]){
                ll is=it.num;
                for(auto ip:sv[it.val]){
                    ll js=cut(fy,ip);
                    ans+=is*js;
                }
            }
            for(node it:se[fx])ins(fy,it.val,it.num);
            pre[fx]=fy;
            siz[fy]+=siz[fx];
        }
    }
}

void solve(){
    scanf("%lld%lld",&n,&m);
    ans=0;
    L(i,1,n+m)pre[i]=i,siz[i]=1;
    L(i,1,n){
        scanf("%lld",&a[i]);
        se[i].insert(node(a[i],1));
    }
    while(m--){
        scanf("%lld%lld%lld",&op,&x,&y);
        if(op==3){
            ll fx=fd(x);//printf("?%lld\n",fx);
            for(auto ip:sv[a[x]]){
                ll js=cut(fx,ip);
                ans-=js;
            }
            del(fx,a[x]);
            a[x]=y;
            for(auto ip:sv[a[x]]){
                ll js=cut(fx,ip);
                ans+=js;
            }
            ins(fx,a[x],1);
        }
        else if(op==2){
            meg(x,y);
        }
        else{
            a[x]=y;
            se[x].insert(node(y,1));
        }
        printf("%lld\n",ans);
    }
}

int main(){
    // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    // cout<<fixed<<setprecision(12);//精度
    ll Case=1;
    init(200000);
    //scanf("%lld",&Case);
    while(Case--)solve();
}

3、【J. Joy of Handcraft】

题目来源:

2020 CCPC 绵阳站 J. Joy of Handcraft

难度:

铜牌🥉-(参照比赛正式队情况)

题意:

给定 n n n 个灯泡,每个灯泡打开关闭状态有一个变化周期,即对于第 i i i 个灯泡,在 [ 2 k t i + 1 , 2 k t i + t i ] [2kt_i+1,2kt_i+t_i] [2kti+1,2kti+ti] 时间内为打开状态,在 [ 2 k t i + t i + 1 , 2 k t i + 2 t i ] [2kt_i+t_i+1,2kt_i+2t_i] [2kti+ti+1,2kti+2ti] 时间内为关闭状态,同时,其有一个权值 x i x_i xi

输出第 j j j 秒时( j ∈ [ 1 , m ] j∈[1,m] j[1,m]),所有打开状态的灯泡中的最大权值。

算法:

权值线段树

思路:

枚举每个周期不同的灯泡的最大值,对于周期为 t t t 的灯泡,我们只需处理 ⌈ m t ⌉ \lceil \frac{m}{t}\rceil tm 个区间,总量级为 m l o g m mlogm mlogm
建立权值线段树,维护区间最大值,每次区间标记覆盖即可。

#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};

ll m,n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N];
double dans;
bool vis[N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y;}q[N];

bool cmp(qq u,qq v){
    return u.y<v.y;
}
bool cmp1(qq u,qq v){
    return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
    return u>v;
}};//shun序

pair<ll,ll>pr;
vector<ll>sv[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,ll>mp;
bitset<M>bi;

struct segment{ll l,r,mx;}trs[N*4];

void push_down(ll k){
    if(trs[k].mx){
        ll l=k*2,r=k*2+1;
        trs[l].mx=max(trs[l].mx,trs[k].mx);
        trs[r].mx=max(trs[r].mx,trs[k].mx);
        trs[k].mx=0; 
    }
}

void bd_tree(ll k,ll l,ll r){
    trs[k].l=l,trs[k].r=r;
    trs[k].mx=0;
    if(l==r){
        trs[k].mx=0;
        return;
    }
    ll mid=(l+r)/2;
    bd_tree(k*2,l,mid);
    bd_tree(k*2+1,mid+1,r);
}

ll query(ll k,ll p){
    if(trs[k].l==trs[k].r){
        return trs[k].mx;
    }
    push_down(k);
    ll mid=(trs[k].l+trs[k].r)/2;
    if(mid>=p)return query(k*2,p);
    else return query(k*2+1,p);
}

void modify(ll k,ll pl,ll pr,ll val){
    if(trs[k].l>=pl&&trs[k].r<=pr){
        trs[k].mx=max(trs[k].mx,val);
        return;
    }
    push_down(k);
    ll mid=(trs[k].l+trs[k].r)/2;
    if(mid>=pl)modify(k*2,pl,pr,val);
    if(mid+1<=pr)modify(k*2+1,pl,pr,val);
}

void solve(){
    scanf("%lld%lld",&n,&m);
    cnt=0;
    bd_tree(1,1,m);
    L(i,1,m+1)a[i]=0;
    L(i,1,n){
        scanf("%lld%lld",&x,&y);
        x=min(x,m);
        a[x]=max(a[x],y);
    }
    L(i,1,m){
        if(a[i])q[++cnt]={i,a[i]};
    }
    sort(q+1,q+cnt+1,cmp);
    L(i,1,cnt){
        for(ll j=1;j<=m;j+=q[i].x*2){
            ll k=min(m,j+q[i].x-1);
            modify(1,j,k,q[i].y);
        }
    }
    L(i,1,m){
        printf("%lld",query(1,i));
        if(i!=m)printf(" ");
        else printf("\n");
    }
}

int main(){
    // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    // cout<<fixed<<setprecision(12);//精度
    ll Case=1;
    scanf("%lld",&Case);
    L(i,1,Case){
        printf("Case #%lld: ",i);
        solve();
    }
}

4、【C. Rencontre】

题目来源:

2020 CCPC 威海站 C. Rencontre

难度:

银牌🥈-(参照比赛正式队情况)

题意:

给定由 n n n 个节点 n − 1 n-1 n1 条边形成的一棵树,给定每条边的边权。有三个人,给定每个人的起点清单,每个人会从自己的清单中等概率的选择一个节点作为起点,求使三人汇集在一点的期望最小花费。

算法:

树dp

思路:

假设三个人的起点分别位于 x , y , z x,y,z x,y,z,那么使三人汇聚一点的最小花费为 w ( x , y ) + w ( x , z ) + w ( y , z ) 2 \frac{w(x,y)+w(x,z)+w(y,z)}{2} 2w(x,y)+w(x,z)+w(y,z),其中 w ( x , y ) w(x,y) w(x,y) 表示点 x x x 到点 y y y 的简单路径的权值和。因此,我们可以将问题拆解为三个子问题:求两人汇聚一点的期望最小花费。
假设 b x b_x bx 表示点 1 1 1 到点 x x x 的简单路径权值和,则 w ( x , y ) = b x + b y − 2 b l c a ( x , y ) w(x,y)=b_x+b_y-2b_{lca(x,y)} w(x,y)=bx+by2blca(x,y),其中 l c a ( x , y ) lca(x,y) lca(x,y) 表示点 x x x 和点 y y y 的最近公共祖先。
对于两个人的起点清单,期望值 = = = 所有方案权值和 / / / 方案数。所有方案权值和可以通过枚举点 x x x 是多少对 ( i , j ) (i,j) (i,j) l c a lca lca 快速求得。
具体的说,假设分别有 m 0 , m 1 m_0,m_1 m0,m1 个起点方案,每个起点分别是 a 0 , i a_{0,i} a0,i i ∈ [ 1 , m 0 ] i∈[1,m_0] i[1,m0])和 a 1 , i a_{1,i} a1,i i ∈ [ 1 , m 1 ] i∈[1,m_1] i[1,m1]),则方案数 = m 0 ∗ m 1 =m_0*m_1 =m0m1,所有方案权值和 = m 1 ∑ i = 1 m 0 b a 0 , i + m 0 ∑ i = 1 m 1 b a 1 , i − ∑ i = 1 m 0 ∑ j = 1 m 1 b l c a ( a 0 , i , a 1 , j ) ① =m_1\sum_{i=1}^{m_0}b_{a_{0,i}}+m_0\sum_{i=1}^{m_1}b_{a_{1,i}}-\sum_{i=1}^{m_0}\sum_{j=1}^{m_1}b_{lca(a_{0,i},a_{1,j})}① =m1i=1m0ba0,i+m0i=1m1ba1,ii=1m0j=1m1blca(a0,i,a1,j)
对于式①,枚举点 x x x ,如果 l c a ( i , j ) = x lca(i,j)=x lca(i,j)=x,那么点 i i i 和点 j j j 一定在点 x x x 的不同儿子分支中。 d f s dfs dfs 时维护以点 x x x 作为根的子树中,第一个人的起点数量以及第二个人的起点数量。具体过程见代码。

#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};

ll m[5],n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N],c[10];
double dans;
bool vis[5][N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;

bool cmp(qq u,qq v){
    return u.x>v.x;
}
bool cmp1(qq u,qq v){
    return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
    return u>v;
}};//shun序

pair<ll,ll>pr;
vector<qq>sv[N];
vec<ll>ss[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,ll>mp;
multiset<ll>se;
set<ll>::iterator it;
bitset<M>bi;

struct edge{
    ll cnt,hed[N],to[N*2],nxt[N*2];
    ll w[N*2];
    void add(ll u,ll v,ll ew){to[++cnt]=v;w[cnt]=ew;nxt[cnt]=hed[u];hed[u]=cnt;}
    void ADD(ll u,ll v,ll ew){add(u,v,ew);add(v,u,ew);}
    void clear(ll n){cnt=0;L(i,1,n)hed[i]=0;}
}eg;

pll dfs(ll u,ll ac,ll i0,ll i1){
    ll n0=vis[i0][u],n1=vis[i1][u],num=n0*n1;
    for(ll i=eg.hed[u];i;i=eg.nxt[i]){
        ll v=eg.to[i];
        if(v!=ac){
            pll tmp=dfs(v,u,i0,i1);
            num+=n0*tmp.se+n1*tmp.fi;
            n0+=tmp.fi,n1+=tmp.se;
        }
    }
    ans-=num*b[u]*2;
    return mkp(n0,n1);
}

void dfs0(ll u,ll ac){
    for(ll i=eg.hed[u];i;i=eg.nxt[i]){
        ll v=eg.to[i];
        if(v!=ac){
            b[v]=b[u]+eg.w[i];
            dfs0(v,u);
        }
    }
}

void solve(){
    scanf("%lld",&n);
    L(i,1,n-1){
        scanf("%lld%lld%lld",&x,&y,&z);
        eg.ADD(x,y,z);
    }
    b[1]=0;
    dfs0(1,0);
    L(i,1,3){
        scanf("%lld",&m[i]);
        c[i]=0;
        L(j,1,m[i]){
            scanf("%lld",&x);
            vis[i][x]=1;
            c[i]+=b[x];
        }
    }
    
    ans=c[1]*m[2]+c[2]*m[1];
    dfs(1,0,1,2);//printf("%lld\n",ans);
    double d0=(double)ans/(double)m[1]/(double)m[2];

    ans=c[1]*m[3]+c[3]*m[1];
    dfs(1,0,1,3);//printf("%lld\n",ans);
    double d1=(double)ans/(double)m[1]/(double)m[3];

    ans=c[2]*m[3]+c[3]*m[2];
    dfs(1,0,2,3);//printf("%lld\n",ans);
    double d2=(double)ans/(double)m[2]/(double)m[3];
    
    dans=(d0+d1+d2)/2.0;
    printf("%.10lf\n",dans);
}

int main(){
    // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    // cout<<fixed<<setprecision(12);//精度
    ll Case=1;
    //scanf("%lld",&Case);
    while(Case--)solve();
}

5、【G. Caesar Cipher】

题目来源:

2020 CCPC 威海站 G. Caesar Cipher

难度:

银牌🥈(参照比赛正式队情况)

题意:

给定一个长度为 n n n 的序列 a a a a i ∈ [ 0 , 65536 ) a_i∈[0,65536) ai[0,65536),要求支持两种操作:

  1. 给定一段区间 [ l , r ] [l,r] [l,r] ,对于所有 i ∈ [ l , r ] i∈[l,r] i[l,r] a i = ( a i + 1 ) m o d    65536 a_i=(a_i+1) \mod 65536 ai=(ai+1)mod65536
  2. 给定三个整数 x , y , l x,y,l x,y,l,询问区间 [ x , x + l − 1 ] [x,x+l-1] [x,x+l1] 与区间 [ y , y + l − 1 ] [y,y+l-1] [y,y+l1] 是否相等。

算法:

势能线段树维护区间哈希

思路:

考虑使用线段树维护区间哈希,对于操作1, a i < 65535 a_i<65535 ai<65535只需将区间哈希值加上 P 0 + P 1 + . . . + P l e n P^0+P^1+...+P^{len} P0+P1+...+Plen l e n len len表示当前维护的区间长度),预处理 P P P 的前缀和即可;而对于 a i = 65535 a_i=65535 ai=65535,我们不能区间操作,直接递归到每个单点进行修改,操作类似势能线段树。
可以证明,对于每个节点最多会产生 8 8 8 个这样的递归操作,因此总复杂度不会超过 O ( n + 8 n l o g n ) O(n+8nlogn) O(n+8nlogn)

#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=2e6+10,M=10;
const ll mod=998244353,mmod=mod-1,W=131;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};

ll m[5],n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N];
double dans;
bool vis[5][N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;

bool cmp(qq u,qq v){
    return u.x>v.x;
}
bool cmp1(qq u,qq v){
    return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
    return u>v;
}};//shun序

pair<ll,ll>pr;
vector<qq>sv[N];
vec<ll>ss[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,ll>mp;

ll w[N],sw[N];
void init(ll n){
    w[0]=1;sw[0]=0;
    L(i,1,n){
        w[i]=w[i-1]*W%mod;
        sw[i]=(sw[i-1]+w[i-1])%mod;
    }
}

struct segment{ll l,r,tag,sum,mx;}trs[N*8];
void push_up(ll k){
    ll l=k*2,r=k*2+1;
    trs[k].sum=(trs[l].sum*w[trs[r].r-trs[r].l+1]%mod+trs[k*2+1].sum)%mod;
    trs[k].mx=max(trs[l].mx,trs[r].mx);
}
void push_down(ll k){
    if(trs[k].tag){
        ll l=k*2,r=k*2+1,tag=trs[k].tag;
        trs[l].tag+=tag;
        trs[r].tag+=tag;
        trs[l].sum=(trs[l].sum+sw[trs[l].r-trs[l].l+1]*tag%mod)%mod;
        trs[r].sum=(trs[r].sum+sw[trs[r].r-trs[r].l+1]*tag%mod)%mod;
        trs[l].mx+=tag;
        trs[r].mx+=tag;
        trs[k].tag=0; 
    }
}
ll cal(ll l0,ll r0,ll l1,ll r1){
    if(l0<l1&&r0>=l1&&r0<=r1)return r0-l1+1;
    else if(l0>=l1&&l0<=r1&&r0>=l1&&r0<=r1)return r1-l1+1;
    else if(l0>=l1&&l0<=r1&&r0>r1)return r1-l0+1;
    else if(l0<l1&&r0>r1)return r1-l1+1;
    else return 0;
}

void bd_tree(ll k,ll l,ll r){
    trs[k].tag=0;
    trs[k].l=l,trs[k].r=r;
    if(l==r){
        trs[k].sum=a[l];
        trs[k].mx=a[l];
        return;
    }
    ll mid=(l+r)/2;
    bd_tree(k*2,l,mid);
    bd_tree(k*2+1,mid+1,r);
    push_up(k);
}

ll query(ll k,ll pl,ll pr){
    if(trs[k].r<pl||trs[k].l>pr)return 0;
    if(trs[k].l>=pl&&trs[k].r<=pr){
        return trs[k].sum;
    }
    push_down(k);
    ll mid=(trs[k].l+trs[k].r)/2;
    if(mid>=pl&&mid+1<=pr){
        ll ml=query(k*2,pl,pr),mr=query(k*2+1,pl,pr),len=cal(pl,pr,trs[k*2+1].l,trs[k*2+1].r);
        return (ml*w[len]%mod+mr)%mod;
    }
    else if(mid>=pl){
        return query(k*2,pl,pr);
    }
    else{
        return query(k*2+1,pl,pr);
    }
}

void modify(ll k,ll pl,ll pr){
    if(trs[k].l==trs[k].r){
        trs[k].mx=(trs[k].mx+1)%65536;
        trs[k].sum=trs[k].mx;
        return;
    }
    if(trs[k].l>=pl&&trs[k].r<=pr&&trs[k].mx<65535){
        trs[k].sum=(trs[k].sum+sw[trs[k].r-trs[k].l+1])%mod;
        trs[k].mx++;
        trs[k].tag+=1;
        return;
    }
    push_down(k);
    ll mid=(trs[k].l+trs[k].r)/2;
    if(mid>=pl)modify(k*2,pl,pr);
    if(mid+1<=pr)modify(k*2+1,pl,pr);
    push_up(k);
}

void solve(){
    scanf("%lld%lld",&n,&t);
    L(i,1,n)scanf("%lld",&a[i]);
    bd_tree(1,1,n);
    while(t--){
        scanf("%lld%lld%lld",&op,&x,&y);
        if(op==1){
            modify(1,x,y);
        }
        else{
            scanf("%lld",&k);
            ll ml=query(1,x,x+k-1),mr=query(1,y,y+k-1);//printf("%lld %lld\n",ml,mr);
            if(ml==mr)printf("yes\n");
            else printf("no\n");
        }
    }
}

int main(){
    // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    // cout<<fixed<<setprecision(12);//精度
    ll Case=1;
    init(500000);
    //scanf("%lld",&Case);
    while(Case--)solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值