8.22普及模拟三

\(\large{T1\ 最大生成树} \ \ \tiny{30 pts}\)

  • 题意: 给定一个点权为\(a_i\) 边权为\(|a_i-a_j|\) 的完全图,求这个图的最大生成树
  • 部分分:
    • 30~50pts:Kruskal求最长路
  • 100pts:贪心,设权值最大点为\(a_{max}\),权值最小点为\(a_{min}\)\(max(|a_{max}-a_i|,|a_{min}-a_i|)\)一定为\(a_i\)的对答案最大贡献
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define int long long
#define frer freopen("in.in","r",stdin);
#define frew freopen("out.out","w",stdout);
using namespace std;
const int Max = 1e5+10;
int n;
int a[Max];
int e[Max];
int m;
int ans;
inline int read(){
    int num=0,fl=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') fl=-1;
            c=getchar();
    }
    while(c >='0'&&c <='9'){
        num=(num<<3)+(num<<1)+(c^48);
        c=getchar();
    }
    return num*fl;
}
inline bool cmp(int a,int b){
    return a>b;
}
signed main(){
    n=read();
    for(int i = 1;i <= n;i++){
        a[i]=read();
    }
    
    sort(a+1,a+n+1,cmp);
    for(int i = 1;i < n;i++){
        e[i]=max(abs(a[1]-a[i]),abs(a[n]-a[i]));
    }
    sort(e+1,e+n+1,cmp);
    for(int i = 1;i < n;i++){
        ans+=e[i];
    }
    printf("%lld",ans);
    return 0;
}

\(\large{T2\ 红黑树} \ \ \tiny{0 pts}\)

  • 题意:给定一棵\(n\)个点的有根树,根为节点1,初始每个点都是红色,有次操作,每次操作会把\(p_i\)变成黑色,保证操作序列\(p\)是一个排列,输出有多少个子树是红黑树。
  • 部分分:
    • 50pts:每次遍历以每个节点为根的子树(没调出来)。
    • 80pts: 暴力跳爹(没打)
  • 100pts: 差分:用\(fir\)记录每个子树第一个黑点出现的时间,用\(fin\)记录每个子树最后一个红点被覆盖的时间。并用差分维护(可以理解为在区间内此子树一直为红黑树)
    • 注意:\(fin\)数组维护差分时为 sum[fin[i]-1+1],“-1”是因为\(fin[i]\)时此子树已经不是红黑树,“+1”是因为差分。
#include <iostream>
#include <cstdio>
#define int long long
#define frer freopen("in.in","r",stdin);
#define frew freopen("out.out","w",stdout);
using namespace std;
const int Max = 1e5+10;
int n;
int ans,f;
int Head[Max],Next[Max],Ver[Max],tot;
int times[Max];
int fir[Max],fin[Max];
int sum[Max];
inline void add(int x,int y){
    Ver[++tot]=y;Next[tot]=Head[x];Head[x]=tot;
}
inline int read(){
    int num=0,fl=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') fl=-1;
            c=getchar();
    }
    while(c >='0'&&c <='9'){
        num=(num<<3)+(num<<1)+(c^48);
        c=getchar();
    }
    return num*fl;
}
inline void dfs(int x){
    fir[x]=times[x],fin[x]=times[x];
    for(int i = Head[x];i;i=Next[i]){
        int y=Ver[i];
        dfs(y);
        fir[x]=min(fir[x],fir[y]);
        fin[x]=max(fin[x],fin[y]);
    }
}
signed main(){
    frer;
    frew;
    n=read();
    for(int i = 1;i < n;i++){
        int x=read();
        add(x,i+1);
    }
    for(int i = 1;i <= n;i++){
        int x=read();
        times[x]=i;
    }
    dfs(1);
    for(int i = 1;i <= n;i++){
        sum[fir[i]]++;
        sum[fin[i]-1+1]--;
    }
    for(int i = 1;i <= n;i++){
        ans+=sum[i];
        printf("%lld ",ans);
    }
    return 0;
}

\(\large{T3\ 校门外的树} \ \ \tiny{0 pts}\)

  • 题意:校门外有 \(n\) 棵树,每棵树有一个初始高度 \(h_i\) 和生长速度 \(v_i\) ,在接下来的 \(m\) 天中,第\(i\)棵树会长高 \(v_i\) ,之后有两种事件可能会发生:
    • 操作一:将 \([l,r]\) 区间内的 \(v_i\) 增加 \(v`\)
    • 操作二:求 \(\sum_{i=l}^{r} h_i\)
    • 对答案膜 \(2^{64}\)
  • 部分分:
    • 30pts:数组&&线段树乱搞
  • 100pts:
    • 不需要每天对维护树的高度,只需要在查询时将初始高度加天数成速度。(\(h_i+i\times v_i\)
    • 可以线段树维护速度,前缀和维护初始高度。查询时,速度默认生效时间为0,但考虑到有修改操作,速度的生效时间应该为修改时,所以速度是错误的。所以我们需要维护一棵线段树,来记录多出来的速度,查询时减掉。
#include <iostream>
#include <cstdio>
#include <cmath>
#define int unsigned long long
#define ls (x<<1)
#define rs (x<<1|1)
#define frer freopen("in.in","r",stdin);
#define frew freopen("out.out","w",stdout);
using namespace std;
const int Max = 5e6+10;
int n,q;
struct node{
    int tag,l,r,sum;
}t[Max*4][3];
int v[Max],h[Max];
inline int read(){
    int num=0,fl=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c >='0'&&c <='9'){
        num=(num<<3)+(num<<1)+(c^48);
        c=getchar();
    }
    return num*fl;;
}
void build(int x,int l,int r){
    t[x][1].l=l,t[x][1].r=r;
    t[x][2].l=l,t[x][2].r=r;    
    if(l==r){
        t[x][1].sum=v[l];
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    t[x][1].sum=t[ls][1].sum+t[rs][1].sum;
    return;
}
void pushDown(int x,int m){
    if(t[x][m].tag){
        t[ls][m].tag=t[ls][m].tag+t[x][m].tag;
        t[ls][m].sum=t[ls][m].sum+(t[ls][m].r-t[ls][m].l+1)*t[x][m].tag;
        t[rs][m].tag=t[rs][m].tag+t[x][m].tag;
        t[rs][m].sum=t[rs][m].sum+(t[rs][m].r-t[rs][m].l+1)*t[x][m].tag;
        t[x][m].tag=0;
    }
}
void add(int x,int l,int r,int m,int num){
    if(t[x][m].l>=l&&t[x][m].r<=r){
        t[x][m].sum+=(t[x][m].r-t[x][m].l+1)*num;
        t[x][m].tag=t[x][m].tag+num;
        return;
    }
    pushDown(x,m);
    int mid=(t[x][m].l+t[x][m].r)>>1;
    if(mid>=l) add(ls,l,r,m,num);
    if(mid<r) add(rs,l,r,m,num);
    t[x][m].sum=t[ls][m].sum+t[rs][m].sum;
    return;
}
int getSum(int x,int l,int r,int m){
    if(t[x][m].l >= l&&t[x][m].r <= r){
        return t[x][m].sum;
    }
    pushDown(x,m);
    int mid=(t[x][m].l+t[x][m].r)>>1,ans=0;
    if(mid>=l) ans=ans+getSum(ls,l,r,m);
    if(mid<r) ans=ans+getSum(rs,l,r,m);
    return ans;
}
signed main(){
    frer;
    frew;
    n=read();q=read();
    for(register int i = 1;i <= n;++i){
        int x=read();v[i]=read();h[i]=x+h[i-1];
    }
    build(1,1,n);
    for(register int i = 1;i <= q;++i){
        int mod=read();
        if(mod==1){
            int ll=read(),rr=read(),num=read();
            add(1,ll,rr,1,num);
            add(1,ll,rr,2,num);
        }
        else{
            int ll=read(),rr=read();
            int ans=h[rr]-h[ll-1]+i*getSum(1,ll,rr,1)-getSum(1,ll,rr,2);
            printf("%llu\n",ans);
        }
    }
    return 0;
}

\(\large{T4\ 种树} \ \ \tiny{0 pts}\)

  • 什么事根号分治?
posted @ 2023-08-23 11:29  tkt  阅读(31)  评论(1编辑  收藏  举报