首页 > 其他分享 >Living-Dream 系列笔记 第81期

Living-Dream 系列笔记 第81期

时间:2024-10-13 13:32:16浏览次数:6  
标签:Living cur fa int dep lca 81 Dream dp

树上差分

概述:擅长在树上一条路径上统计边或者点的信息。

下文令差分数组为 \(d_i\),\(lca\) 为路径两端点的 LCA,\(fa_i\) 为 \(i\) 的父亲。

  • 按边的差分

    将 \(a\) 到 \(b\) 的路径上的边权加 \(val\):

\[d_a+val \to d_a\\ d_b+val \to d_b\\ d_{lca} - 2 \times val \to d_{lca} \]

  • 按点的差分:

    将 \(a\) 到 \(b\) 的路径上的点权加 \(val\):

\[d_a+val \to d_a\\ d_{lca}-val \to d_{lca}\\ d_b+val \to d_b\\ d_{fa_{lca}}-val \to d_{fa_{lca}} \]

CF191C

一眼树剖。

容易发现这是一道绿题,因此不需要使用如此复杂的数据结构。

又因为这是统计边的信息,考虑树上差分。

其实这就是个板子,相当于每次旅行将 \(a \to b\) 路径上的边边权均 \(+1\)。直接按上文公式做即可。

注意因为差分数组是挂在点上的,所以需要维护一个边和点之间的映射。

code
//
//  CF191C.cpp
//  
//
//  Created by _XOFqwq on 2024/10/11.
//

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

const int N=1e5+5;
int n,k;
int id[N],dp[N][31],diff[N],dep[N];
vector<pair<int,int> > G[N];

void init_lca(int cur,int fa){
    dep[cur]=dep[fa]+1;
    dp[cur][0]=fa;
    for (int i=1; (1<<i)<=dep[cur]; i++) {
        dp[cur][i]=dp[dp[cur][i-1]][i-1];
    }
    for (auto [nxt,i] : G[cur]) {
        if (nxt==fa) {
            continue;
        }
        id[i]=nxt;
        init_lca(nxt,cur);
    }
}
int get_lca(int x,int y){
    if (dep[x]<dep[y]) {
        swap(x,y);
    }
    for (int i=21; i>=0; i--) {
        if (dep[dp[x][i]]>=dep[y]) {
            x=dp[x][i];
        }
    }
    if (x==y) {
        return x;
    }
    for (int i=21; i>=0; i--) {
        if (dp[x][i]!=dp[y][i]) {
            x=dp[x][i],y=dp[y][i];
        }
    }
    return dp[x][0];
}
void dfs(int cur,int fa){
    for (auto [nxt,i] : G[cur]) {
        if (nxt==fa) {
            continue;
        }
        dfs(nxt,cur);
        diff[cur]+=diff[nxt];
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n;
    for (int i=1,u,v; i<n; i++) {
        cin>>u>>v;
        G[u].push_back({v,i});
        G[v].push_back({u,i});
    }
    init_lca(1,0);
    cin>>k;
    for (int i=1,x,y; i<=k; i++) {
        cin>>x>>y;
        int lca=get_lca(x,y);
        diff[x]++;
        diff[y]++;
        diff[lca]-=2;
    }
    dfs(1,0);
    for (int i=1; i<n; i++) {
        cout<<diff[id[i]]<<' ';
    }
    return 0;
}

P2680

还是很巧妙的一道题。

最短时间取决于 \(m\) 条路径的最大边权和,我们要最小化这个东西,直接考虑二分。

对于 \(m\) 条路径,只有边权和 \(> mid\) 的才有必要使用虫洞,设它们的数量为 \(tot\)。

我们要将这 \(tot\) 条路径的边权和全部变得 \(\le mid\),因此只能选所有路径的公共边变成虫洞,并且还得选边权最大的。

找出公共边等价于找出被覆盖 \(tot\) 次的边,直接树上差分即可。

然后这题就做完了。需要适当卡常,详见代码。

code
//
//  P2680.cpp
//
//
//  Created by _XOFqwq on 2024/10/11.
//

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

const int N=3e5+5;
int n,m,tot,w[N];
int ddep[N],dep[N],dp[N][31],id[N],to[N],diff[N];
struct PATH{
    int u,v,dis,lca;
}path[N];
struct EDGE{
    int v,w,e;
};
vector<EDGE> G[N];

namespace fastIO{
    #define BUF_SIZE 1000000
    bool IOerror=0;
    inline char nc() {
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if(p1==pend){
            p1=buf;
            pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if(pend==p1){
                IOerror=1;
                return -1;
            }
        }
        return *p1++;
      }
      inline bool blank(char ch) {
          return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
      }
      inline void read(int &x) {
          char ch;
          while(blank(ch=nc()));
          if(IOerror)    return;
          for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=(x<<3)+(x<<1)+ch-'0');
      }
      #undef BUF_SIZE
};
using namespace fastIO; //卡常 1

bool cmp(PATH x,PATH y){
    return x.dis>y.dis;
}
void init_lca(int cur,int fa){
    dep[cur]=dep[fa]+1;
    dp[cur][0]=fa;
    for (int i=1; (1<<i)<=dep[cur]; i++) {
        dp[cur][i]=dp[dp[cur][i-1]][i-1];
    }
    for (auto [v,w,e] : G[cur]) {
        if (v==fa) {
            continue;
        }
        id[e]=v;
        to[v]=e;
        ddep[v]=ddep[cur]+w;
        init_lca(v,cur);
    }
}
int get_lca(int x,int y){
    if (dep[x]<dep[y]) {
        swap(x,y);
    }
    for (int i=21; i>=0; i--) {
        if (dep[dp[x][i]]>=dep[y]) {
            x=dp[x][i];
        }
    }
    if (x==y) {
        return x;
    }
    for (int i=21; i>=0; i--) {
        if (dp[x][i]!=dp[y][i]) {
            x=dp[x][i],y=dp[y][i];
        }
    }
    return dp[x][0];
}
void dfs(int cur,int fa){
    for (auto [v,w,e] : G[cur]) {
        if (v==fa) {
            continue;
        }
        dfs(v,cur);
        diff[cur]+=diff[v];
    }
}
bool check(int x){
    tot=0;
    memset(diff,0,sizeof diff);
    for (int i=1; i<=m&&path[i].dis>x; i++,tot++) {
        diff[path[i].u]++;
        diff[path[i].v]++;
        diff[path[i].lca]-=2;
    }
    if (!tot) {
        return 1;
    }
    dfs(1,0);
    int mx=-1e9;
    for (int i=1; i<n; i++) {
        if (diff[id[i]]==tot) {
            mx=max(mx,w[i]);
        }
    }
    return path[1].dis-mx<=x;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    read(n),read(m);
    int mxw=-1e9;
    for (int i=1,u,v; i<n; i++) {
        read(u),read(v),read(w[i]);
        G[u].push_back({v,w[i],i});
        G[v].push_back({u,w[i],i});
        mxw=max(mxw,w[i]);
    }
    init_lca(1,0);
    for (int i=1,u,v; i<=m; i++) {
        read(path[i].u),read(path[i].v);
        path[i].lca=get_lca(path[i].u,path[i].v);
        path[i].dis=ddep[path[i].u]+ddep[path[i].v]-2*ddep[path[i].lca];
    }
    sort(path+1,path+m+1,cmp);
    int l=path[1].dis-mxw-1,r=path[1].dis+1; //卡常 2
    while (l+1<r) {
        int mid=(l+r)>>1;
        if (check(mid)) {
            r=mid;
        } else {
            l=mid;
        }
    }
    cout<<r;
    return 0;
}

P8201

更加巧妙的一道题。

首先推式子,

令 \(dep_i\) 表示从根节点到 \(i\) 的路径上的点权异或和:

\[dis_{t,a} \oplus dis_{t,b}\\ =dep_a \oplus dep_b \oplus w_{lca} \oplus w_t\\ =k \]

(\(dep_a \oplus dep_b\) 把 \(lca\) 的权值抵消了,要异或回来;\(t\) 本身应当被抵消,因此要再异或一次以抵消掉它)

也即:

\[dep_a \oplus dep_b \oplus w_{lca} \oplus k=w_t \]

问题转化为有多少个点 \(t\) 满足上式。

我们将询问离线,然后一遍 dfs 统计出从根节点到每个点的路径上当前点权出现次数,询问时进行求一下 \(a \to son_{lca}\) 与 \(b \to lca\) 这两段的出现次数(前缀和),拼起来判断是否 \(>0\) 即可。

(对于树上信息问题,先推式子,接着求出从根节点出发的信息,然后利用前缀和 / 差分之类的东西维护)

code
//
//  P8201.cpp
//  
//
//  Created by _XOFqwq on 2024/10/13.
//

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

const int N=5e5+5;
int n,m;
int a[N],b[N],lca[N],dep[N],dp[N][31];
long long k[N],w[N],dis[N];
vector<int> qry[N],G[N];
unordered_map<int,long long> cnt,ans[N];

void init_LCA(int cur,int fa){
    dis[cur]=dis[fa]^w[cur];
    dep[cur]=dep[fa]+1;
    dp[cur][0]=fa;
    for (int i=1; (1<<i)<=dep[cur]; i++) {
        dp[cur][i]=dp[dp[cur][i-1]][i-1];
    }
    for (int i : G[cur]) {
        if (i==fa) {
            continue;
        }
        init_LCA(i,cur);
    }
}
int get_LCA(int x,int y){
    if (dep[x]<dep[y]) {
        swap(x,y);
    }
    for (int i=21; i>=0; i--) {
        if (dep[dp[x][i]]>=dep[y]) {
            x=dp[x][i];
        }
    }
    if (x==y) {
        return x;
    }
    for (int i=21; i>=0; i--) {
        if (dp[x][i]!=dp[y][i]) {
            x=dp[x][i],y=dp[y][i];
        }
    }
    return dp[x][0];
}
void dfs(int cur,int fa){
    cnt[w[cur]]++;
    for (int i : qry[cur]) {
        ans[cur][i]=cnt[i];
    }
    for (int i : G[cur]) {
        if (i==fa) {
            continue;
        }
        dfs(i,cur);
    }
    cnt[w[cur]]--;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m;
    for (int i=1; i<=n; i++) {
        cin>>w[i];
    }
    for (int i=1,u,v; i<n; i++) {
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    init_LCA(1,0);
    for (int i=1; i<=m; i++) {
        cin>>a[i]>>b[i]>>k[i];
        lca[i]=get_LCA(a[i],b[i]);
        long long val=dis[a[i]]^dis[b[i]]^w[lca[i]]^k[i];
        qry[a[i]].push_back(val);
        qry[b[i]].push_back(val);
        qry[lca[i]].push_back(val);
        qry[dp[lca[i]][0]].push_back(val);
    }
    dfs(1,0);
    for (int i=1; i<=m; i++) {
        long long val=dis[a[i]]^dis[b[i]]^w[lca[i]]^k[i];
        cout<<(ans[a[i]][val]-ans[lca[i]][val]+ans[b[i]][val]-ans[dp[lca[i]][0]][val]?"yEs\n":"nO\n");
    }
    return 0;
}

标签:Living,cur,fa,int,dep,lca,81,Dream,dp
From: https://www.cnblogs.com/XOF-0-0/p/18462198

相关文章

  • 洛谷P8818 [CSP-S 2022] 策略游戏
    Problem给出两个数组A,B,进行q次询问,每次分别给出这两个数组的某个区间l1,r1,l2,r2,也就是\(A_{l1\simr1}\)与\(B_{l2\simr2}\),有两位同学小L和小Q分别从A,B的以上区间中选一个数,而两数乘积为此次操作的分数,小L希望分数大,小Q希望分数小,请问他们每次以最优策略进行游戏,分数将会......
  • Springboot一个小说阅读APP的设计与实现--48151(免费领源码)可做计算机毕业设计JAVA、PH
    摘 要大数据时代下,数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求,利用互联网服务于其他行业,促进生产,已经是成为一种势不可挡的趋势。在小说在线阅读的需求下,开发一款小说阅读APP,将复杂的系统进行拆分,能够实现对需求的变化快速响应、系统稳定性的保障,能保......
  • COMP3811 Computer Graphics
    SchoolofComputing:assessmentbriefModuletitleComputerGraphicsModulecodeCOMP3811AssignmenttitleCoursework1AssignmenttypeanddescriptionProgrammingassignment:GraphicsfundamentalsRationaleThecourseworkrevolvesaroundfundamentalgra......
  • 如何查看GB28181流媒体平台LiveGBS中对GB28181实时视频数据统计的负载信息
    @目录1、负载信息2、负载信息说明3、会话列表查看3.1、会话列表4、停止会话5、搭建GB28181视频直播平台1、负载信息实时展示直播、回放、播放、录像、H265、级联等使用数目2、负载信息说明直播:当前推流到平台的实时视频数目回放:当前推流到平台的回放视频数目播放:当前观看......
  • 海康大华宇视等摄像头/执法记录仪等设备通过GB28181注册到LiveGBS流媒体平台,如何实时
    @目录1、如何监听设备状态2、device订阅2.1、设备上线消息2.2、设备离线消息2.2、通道上线消息2.2、通道离线消息3、订阅示例3.1、连接REDIS3.2、订阅device示例3.3、设备上线示例3.3.1、注册上线后3.4、设备离线示例3.4.1、注销离线后4、更多4.1、如何切换redis5、搭建GB28181视......
  • Idea android应用kotlin-stdlib-1.8.20 kotlin-stdlib-jdk81.6.21冲突
    Ideaandroid应用kotlin-stdlib-1.8.20kotlin-stdlib-jdk81.6.21冲突idea中开发android应用,安装android插件后,新建项目,然后各种包更新,最后运行时提示kotlin-stdlib-1.8.20kotlin-stdlib-jdk8:1.6.21冲突错误如下:FAILURE:Buildfailedwithanexception.Whatwentwrong:......
  • jsp大理美食秘境购物网站的设计与实现vg81b(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,菜系,美食餐厅,餐厅类型,美食信息,加盟信息,餐饮订单开题报告内容一、项目背景随着旅游业的蓬勃发展,大理作为国内外知名的旅游胜地,吸引了大量游客前来探......
  • dreamweaver如何保存网页
    在Dreamweaver中保存网页可以通过以下几个简单的步骤来完成:打开或编辑网页:首先确保你已经打开了你想要保存的网页文件。选择保存选项:如果你是在Windows系统上使用Dreamweaver,可以选择菜单栏中的文件-> 保存,或者直接使用快捷键Ctrl+S。对于Mac用户,可以从菜单栏选择文件......
  • 洛谷题单指南-字符串-P1481 魔族密码
    原题链接:https://www.luogu.com.cn/problem/P1481题意解读:在n个字符串中找到最长的词链长度,定义字符串a、b、c可以形成词链,即a是b的前缀、b是c的前缀。解题思路:1、Trie树定义Trie树,也称前缀树、字典树,核心思想是将字符串拆解为单个字符,每个字符是树的一条边,节点是字符添加到树......
  • 【刷题笔记】[ABC281G] Farthest City
    【刷题笔记】[ABC281G]FarthestCity题意求构造一个没有重边和自环【简单联通】的无向连通图,使得\(d[n]\)严格大于\(d[i]\),问有几种构造方案思路一道\(DP\)好题\(DP\)有\(2\)种题型,求最优值问题,和计数问题。本题为计数问题。因为在边权为1的最短路中\[d[i]=d[i-1]+1\]所......