20190312

 

第一次打字竞赛QwQ

T1

内存限制:512 MiB
时间限制:1000 ms
由于 zzq 太懒了,所以这题没有题目背景。
有一棵树,树上有 $n$ 个点,每条边上有一个非负边权。
在这 $n$ 个点中有 $k$ 个特殊点,其中 $k$ 为偶数。定义两个点的距离为它们在树上的简单路径上的边权之和。你需要将这 $k$ 个点配成 $\frac{k}{2}$ 个互不相交的对,并最大化每一对点的距离之和。
$1 \leq n,c \leq 10^5$

题解

最终所有的路径会交于一点,如果没有交于一点的话,交于一点更优
所以只要一个点,如果以这个点为根,最大的子树内含有的配对点数 $\leq \frac{k}{2}$ ,则以这个点为根去配对即可

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k,hd[N],nx[N*2],t,V[N*2],sz[N],ax[N],now;
bool vis[N];stack<int>g[N];
struct O{
    int x,s;
    friend bool operator < (const O& A,const O& B){
        return A.s<B.s;
    }
};
priority_queue<O>q;
void add(int u,int v){
    V[++t]=v;nx[t]=hd[u];hd[u]=t;
}
void get(int x,int fa){
    if (vis[x]) g[now].push(x);
    for (int i=hd[x];i;i=nx[i])
        if (V[i]!=fa) get(V[i],x);
}
void dfs(int x,int fa){
    sz[x]=vis[x];
    for (int i=hd[x];i;i=nx[i])
        if (V[i]!=fa)
            dfs(V[i],x),sz[x]+=sz[V[i]],
            ax[x]=max(ax[x],sz[V[i]]);
    ax[x]=max(ax[x],k-sz[x]);
    if ((ax[x]<<1)<=k){
        for (int i=hd[x];i;i=nx[i]){
            now=V[i],get(V[i],x);
            int s=g[V[i]].size();
            if (s) q.push(O{V[i],s});
        }
        if (vis[x])
            g[x].push(x),q.push((O){x,1});
        while(!q.empty()){
            int A=q.top().x;q.pop();
            int B=q.top().x;q.pop();
            printf("%d %d\n",g[A].top(),g[B].top());
            g[A].pop();g[B].pop();
            int sA=g[A].size(),sB=g[B].size();
            if (sA) q.push((O){A,sA});
            if (sB) q.push((O){B,sB});
        }
        exit(0);
    }
}
int main(){
    scanf("%d%d",&n,&k);
    for (int x,y,z,i=1;i<n;i++)
        scanf("%d%d%d",&x,&y,&z),
        add(x,y),add(y,x);
    for (int x,i=1;i<=k;i++)
        scanf("%d",&x),vis[x]=1;
    dfs(1,0);return 0;
}

 

T2

内存限制:256 MiB
时间限制:1000 ms
给定字符集大小 $S$ ,问有多少个长度为 $N$ 的字符串不存在长度 $>1$ 的回文后缀。
答案对 $M$ 取模。
$1 \leq n \leq 10^7,1\leq S < M \leq 2^{30}-1$

题解

将字符串倒着看,设 $f_i$ 表示前 $i$ 位且满足条件的字符串的方案数
则 $f_i=f_{i-1} \times s - f_{\frac{i+1}{2}}$
(前 $\frac{i+1}{2}$ 不为回文,将其反过来为回文

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e7+5;
LL s,m,f[N];int n;
int main(){
    scanf("%d%lld%lld",&n,&s,&m);
    f[0]=1;for (int i=1;i<=n;i++)
        f[i]=(f[i-1]*s-f[(i+1)/2])%m;
    return printf("%lld\n",f[n]),0;
}

 

T3

内存限制:512 MiB
时间限制:1000 ms
有一棵 $n$ 个点的树,每个节点上有一个权值 $w_i$ ,最开始根为 $1$ 号点。现在有 $3$ 种类型的操作:
· 1 $root$ , 表示将根设为 $root$ 。
· 2 $u$ $v$ $x$ , 设 $u,v$ 的最近公共祖先为 $p$ , 将 $p$ 的子树中的所有点的权值加上 $x$ 。
· 3 $u$ ,查询 $u$ 的子树中的所有点的权值和。
对于每个 3 操作,输出答案。
$1 \leq n,q \leq 3 \times 10^5,-10^7 \leq w_i,x \leq 10^7$

题解

没有真的换根呦
对于 2 操作,我们可以进行一下分讨
设 $l=lca(u,v)$

  1. $u$ 在 $root$ 子树内, $v$ 在 $root$ 子树内
    如果 $l=rt$ 就是全部 $+x$ ,否则就是 $l$ 子树 $+x$
  2. $u$ 在 $root$ 子树内, $v$ 不在 $root$ 子树内/ $u$ 不在 $root$ 子树内, $v$ 在 $root$ 子树内
    全部 $+x$
  3. $u$ 不在 $root$ 子树内, $v$ 不在 $root$ 子树内
    如果 $l$ 不为 $root$ 的祖先,就是 $l$ 子树 $+x$
    否则先全部 $+x$ ,然后找到 $root$ 到 $l$ 的路径上最后一个不在 $u$ 到 $v$ 这条路径上的点 $w$ , $w$ 子树 $+x$

对于3 操作进行类似的分讨,即可过题

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+5;
int rt,q,w[N],n,c,f[N*2][22],hd[N],nx[N*2],V[N*2],t,tt,id[N],dfn[N],e[N],d[N],ot[N],lg[N*2],fa[N][22];
LL s[N*4],ans,tg[N*4];
void add(int u,int v){
    V[++t]=v;nx[t]=hd[u];hd[u]=t;
}
void dfs(int x,int F){
    f[++c][0]=x;d[x]=d[F]+1;fa[x][0]=F;
    for (int i=1;fa[fa[x][i-1]][i-1];i++)
        fa[x][i]=fa[fa[x][i-1]][i-1];
    dfn[id[x]=++tt]=x;e[x]=c;
    for (int i=hd[x];i;i=nx[i])
        if (V[i]!=F)
            dfs(V[i],x),f[++c][0]=x;
    ot[x]=tt;
}
int lca(int l,int r){
    if (l>r) swap(l,r);int i=log2(r-l+1);
    if (d[f[l][i]]<d[f[r-(1<<i)+1][i]])
        return f[l][i];
    return f[r-(1<<i)+1][i];
}
bool pd(int x,int y){
    return (id[x]>=id[y] && id[x]<=ot[y]);
}
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
void pushdown(int k,int l,int r){
    LL v=tg[k];tg[k]=0;
    s[Ls]+=v*(mid-l+1);tg[Ls]+=v;
    s[Rs]+=v*(r-mid);tg[Rs]+=v;
}
void build(int k,int l,int r){
    if (l==r) s[k]=w[dfn[l]];
    else
        build(Ls,l,mid),
        build(Rs,mid+1,r),
    s[k]=s[Ls]+s[Rs];
}
void update(int k,int l,int r,int L,int R,int v){
    if (L<=l && r<=R){
        s[k]+=1ll*v*(r-l+1);tg[k]+=v;return;
    }
    if (tg[k]) pushdown(k,l,r);
    if (mid>=L) update(Ls,l,mid,L,R,v);
    if (mid<R) update(Rs,mid+1,r,L,R,v);
    s[k]=s[Ls]+s[Rs];
}
LL query(int k,int l,int r,int L,int R){
    if (L<=l && r<=R) return s[k];
    if (tg[k]) pushdown(k,l,r);
    if (mid<L) return query(Rs,mid+1,r,L,R);
    else if (mid>=R) return query(Ls,l,mid,L,R);
    return query(Rs,mid+1,r,L,R)+query(Ls,l,mid,L,R);
}
int main(){
    for (int i=0;i<20;i++) lg[1<<i]=i;
    scanf("%d%d",&n,&q);rt=1;
    for (int i=1;i<=n;i++)
        scanf("%d",&w[i]);
    for (int x,y,i=1;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1,0);build(1,1,n);
    for (int i=c;i;i--)
        for (int j=1;i+(1<<j)<=c+1;j++)
            if (d[f[i][j-1]]<d[f[i+(1<<(j-1))][j-1]])
                f[i][j]=f[i][j-1];
            else f[i][j]=f[i+(1<<(j-1))][j-1];
    for (int op,u,v,l,x;q--;){
        scanf("%d",&op);
        if (op<2) scanf("%d",&rt);
        else if (op>2){
            scanf("%d",&l);
            if (pd(l,rt)){
                if (l==rt) ans=query(1,1,n,1,n);
                else ans=query(1,1,n,id[l],ot[l]);
            }
            else{
                if (lca(e[l],e[rt])==l){
                    ans=query(1,1,n,1,n);
                    l=d[rt]-d[l]-1;v=rt;
                    for (;l;l-=l&-l) v=fa[v][lg[l&-l]];
                    ans-=query(1,1,n,id[v],ot[v]);
                }
                else ans=query(1,1,n,id[l],ot[l]);
            }
            printf("%lld\n",ans);
        }
        else{
            scanf("%d%d%d",&u,&v,&x);
            if (pd(u,rt)){
                if (pd(v,rt)){
                    l=lca(e[u],e[v]);
                    if (l==rt) update(1,1,n,1,n,x);
                    else update(1,1,n,id[l],ot[l],x);
                }
                else update(1,1,n,1,n,x);
            }
            else{
                if (pd(v,rt)) update(1,1,n,1,n,x);
                else{
                    l=lca(e[u],e[v]);
                    if (pd(rt,l)){
                        update(1,1,n,1,n,x);l=rt;
                        for (int i=19;~i;i--)
                            if (fa[l][i] && !pd(u,fa[l][i]) && !pd(v,fa[l][i]))
                                l=fa[l][i];
                        update(1,1,n,id[l],ot[l],-x);
                    }
                    else update(1,1,n,id[l],ot[l],x);
                }
            }
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/xjqxjq/p/10544779.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值