20250815 杂题

P4643 [国家集训队] 阿狸和桃子的游戏

图片

发现每个边权可以分摊到两个点上。

因为你考虑,这样的话差值不会变,和也不会变。


匿名

图片

首先dij是可以多源的。

所以预处理出来每个点举例守卫的距离。

然后考虑边权是两个点权的较小值,发现你求出来最大生成树的话,最优路径一定在上面(一个套路?可惜我不知道)

然后你每次在生成树上做即可。


图片

考虑序列分治,最大值在一边的话,另一边一定是连续前缀,所以问题可以转化为,维护一个数据结构,支持插入/删除一个数 xx,查询当前集合内含有的某个数 xx 的子集/超集数量。。

图片


P3332 [ZJOI2013] K大数查询

整体二分

抽象在于。

二分的是答案,也就是l,r,mid是答案,但是你要处理的L,R,是序列,所以比较难理解。

你要实现一个函数slove(l,r);

假设你当前二分到mid,你需要将当前区间转化为两个可以求解的子问题。

具体的,对于修改操作,因为当前答案是mid,所以修改操作显然只会对后面的产生贡献,进行修改实施插到后半部分,否则放到前半部分。

查询操作,因为当前答案是问题的l,r的目前值,所以对于小于mid的查询,放到左面,否则减去mid放到右面。

对于处理修改,你需要维护支持区间加,区间清零,区间查询的线段树,每次查询

然后递归即可。

其中线段树维护的是当前的序列状态,对于一个点i,线段树是答案为在答案为mid的情况下,序列i的点目前有多少个。



#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int maxn = 1e6 + 10;
struct Ask{
    int op, l, r, id;
    int val;
}q[maxn], tl[maxn], tr[maxn];

int tag[maxn << 2], rec[maxn << 2];
int ans[maxn];

int sum[maxn];

int n, m, Q;

inline int ls(int x){return x << 1;}
inline int rs(int x){return x << 1 | 1;}

inline void pushdown(int l,int r,int d){
    if(rec[d]){
        rec[d] = 0;
        tag[ls(d)] = tag[rs(d)] = sum[ls(d)] = sum[rs(d)] = 0;
        rec[ls(d)] = 1, rec[rs(d)] = 1;
    }
    if(tag[d]){
        int mid = (l + r) >> 1;
        tag[ls(d)] += tag[d];
        tag[rs(d)] += tag[d];
        sum[ls(d)] += tag[d] * (mid - l + 1);
        sum[rs(d)] += tag[d] * (r - mid);
        tag[d] = 0;
    }
}

inline void pushup(int d){
    sum[d] = sum[ls(d)] + sum[rs(d)];
}

void add(int L,int R,int l,int r,int d,int k){
    if(L <= l && r <= R){
        tag[d] += k;
        sum[d] += (r - l + 1) * k;
        return;
    }
    pushdown(l, r, d);
    int mid = (l + r) >> 1;
    if(L <= mid) add(L,R,l,mid,ls(d),k);
    if(R > mid) add(L,R,mid+1,r,rs(d),k);
    pushup(d);
}

int query(int L,int R,int l,int r,int d){
    if(L <= l && r <= R) return sum[d];
    int mid = (l + r) >> 1;
    int res = 0;
    pushdown(l,r,d);
    if(L <= mid) res += query(L,R,l,mid,ls(d));
    if(R > mid) res += query(L,R,mid+1,r,rs(d));
    return res;
}

void slove(int st,int ed,int l,int r){
    if(l == r){
        for(int i = st;i <= ed;i++){
            if(q[i].op == 2) ans[q[i].id] = l;
        }
        return;
    }
    int mid = (l + r) >> 1;
    bool fl = 0, fr = 0;
    int L = 0, R = 0;
    rec[1] = 1;
    tag[1] = sum[1] = 0;
    for(int i = st;i <= ed;i++){
        if(q[i].op == 1){
            if(q[i].val > mid){
                add(q[i].l,q[i].r,1,n,1,1);
                tr[++R] = q[i];
            }else{
                tl[++L] = q[i];
            }
        }
        else{
            int val = query(q[i].l,q[i].r,1,n,1);
            if(val < q[i].val){
                q[i].val -= val;
                fl = 1;
                tl[++L] = q[i];
            }
            else{
                fr = 1;
                tr[++R] = q[i];
            }
        }
    }
    for(int i = 1;i <= L;i++) q[st+i-1] = tl[i];
    for(int i = L+1;i <= L+R;i++) q[st+i-1] = tr[i - L];
    if(fl) slove(st,st+L-1,l,mid);
    if(fr) slove(st+L,ed,mid+1,r);
}

signed main(){
    ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i = 1;i <= m;i++){
        cin>>q[i].op>>q[i].l>>q[i].r>>q[i].val;
        if(q[i].op == 2) q[i].id = ++Q;
    }
    slove(1,m,-n,n);
    for(int i = 1;i <= Q;i++){
        cout<<ans[i]<<endl;
    }
    return 0;
}


P9130 [USACO23FEB] Hungry Cow P

一种单侧递归线段树,类似楼房重建。

图片

图片

图片

图片

注意,nop更新在pull时,可以保证是及时的。

nopB是A的rem和B的num的贡献之和

图片

posted @ 2025-08-15 22:19  Dreamers_Seve  阅读(12)  评论(0)    收藏  举报