洛谷P4216 [SCOI2015]情报传递(树剖+主席树)

本文深入探讨了主席树和树剖算法在解决特定路径查询问题中的应用,通过实例展示了如何利用这两种算法高效地处理情报员权值的路径查询,同时提供了完整的代码实现。

传送门

 

我们可以进行离线处理,把每一个情报员的权值设为它开始收集情报的时间

那么设询问的时间为$t$,就是问路径上有多少个情报员的权值小于等于$t-c-1$

这个只要用主席树上树就可以解决了,顺便用树剖求一下LCA

 1 //minamoto
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 5 char buf[1<<21],*p1=buf,*p2=buf;
 6 inline int read(){
 7     #define num ch-'0'
 8     char ch;bool flag=0;int res;
 9     while(!isdigit(ch=getc()))
10     (ch=='-')&&(flag=true);
11     for(res=num;isdigit(ch=getc());res=res*10+num);
12     (flag)&&(res=-res);
13     #undef num
14     return res;
15 }
16 char sr[1<<21],z[20];int C=-1,Z;
17 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
18 inline void print(int x,char ch){
19     if(C>1<<20)Ot();
20     while(z[++Z]=x%10+48,x/=10);
21     while(sr[++C]=z[Z],--Z);sr[++C]=ch;
22 }
23 const int N=3e5+5,M=N*32;
24 struct node{int u,v,c,id;}q[N];
25 int head[N],Next[N],ver[N],tot;
26 inline void add(int u,int v){
27     ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
28 }
29 int L[M],R[M],sum[M],rt[N],cnt;
30 void update(int last,int &now,int l,int r,int x){
31     sum[now=++cnt]=sum[last]+1;
32     if(l==r) return;
33     int mid=(l+r)>>1;
34     if(x<=mid) R[now]=R[last],update(L[last],L[now],l,mid,x);
35     else L[now]=L[last],update(R[last],R[now],mid+1,r,x);
36 }
37 int fa[N],dep[N],top[N],sz[N],son[N],val[N],root,n,m,k;
38 void dfs1(int u){
39     sz[u]=1,dep[u]=dep[fa[u]]+1;
40     update(rt[fa[u]],rt[u],1,m,val[u]);
41     for(int i=head[u];i;i=Next[i]){
42         int v=ver[i];
43         dfs1(v),sz[u]+=sz[v];
44         if(sz[son[u]]<sz[v]) son[u]=v;
45     }
46 }
47 void dfs2(int u,int t){
48     top[u]=t;
49     if(son[u]){
50         dfs2(son[u],t);
51         for(int i=head[u];i;i=Next[i])
52         if(ver[i]!=son[u]) dfs2(ver[i],ver[i]);
53     }
54 }
55 inline int LCA(int u,int v){
56     while(top[u]!=top[v])
57     dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
58     return dep[u]<dep[v]?u:v;
59 }
60 int query(int u,int v,int lca,int lca_fa,int l,int r,int x){
61     if(r<=x) return sum[u]+sum[v]-sum[lca]-sum[lca_fa];
62     if(l>x) return 0;
63     int mid=(l+r)>>1,res=0;
64     if(x>=l) res+=query(L[u],L[v],L[lca],L[lca_fa],l,mid,x);
65     if(x>mid) res+=query(R[u],R[v],R[lca],R[lca_fa],mid+1,r,x);
66     return res;
67 }
68 int main(){
69 //    freopen("testdata.in","r",stdin);
70     n=read();
71     for(int i=1;i<=n;++i){fa[i]=read(),add(fa[i],i);if(fa[i]==0) root=i;}
72     m=read();for(int i=1;i<=n;++i) val[i]=m;
73     for(int i=1;i<=m;++i){
74         int op=read(),p;
75         if(op&1) q[++k].u=read(),q[k].v=read(),q[k].c=read(),q[k].id=i;
76         else val[p=read()]=i;
77     }
78     dfs1(root),dfs2(root,root);
79     for(int i=1;i<=k;++i){
80         int u=q[i].u,v=q[i].v,lca=LCA(u,v),lca_fa=fa[lca];
81         print(dep[u]+dep[v]-2*dep[lca]+1,' ');
82         int t=q[i].id-q[i].c-1;
83         print(t>0?query(rt[u],rt[v],rt[lca],rt[lca_fa],1,m,t):0,'\n');
84     }
85     Ot();
86     return 0;
87 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9797570.html

### 解题思路 洛谷 P4160 [SCOI2009] 生日快乐 这道题的核心在于递归与分治策略。题目要求将一个矩形蛋糕切成若干块,使得每一块的长宽比的最大值最小。由于每一块的长宽比是独立的,因此可以通过递归的方法,将问题分解为子问题来求解。 #### 核心思路: 1. **递归切分**:每次将蛋糕分成两部分,并递归地对这两部分进行同样的操作,直到只剩一块为止。 2. **枚举切分方式**:对于每一层递归,需要枚举所有可能的切分方式(横向或纵向),以及每一块的大小比例。 3. **取最大值与最小值**:每一步递归中,选择切分方式使得两部分的最大长宽比尽可能小。 #### 关键点: - **长宽比处理**:为了保证长宽比的计算准确,需要确保长边在分子,短边在分母。 - **切分方式枚举**:枚举所有可能的切分比例,确保没有遗漏。 - **递归终止条件**:当只剩一块时,直接返回当前长宽比。 ### 代码示例 以下是一个完整的代码实现,展示了如何通过递归方法解决这个问题: ```cpp #include <iostream> #include <cstdio> #include <algorithm> using namespace std; int x, y, n; // 计算最大公约数 int gcd(int x, int y) { if (y == 0) return x; return gcd(y, x % y); } // 递归函数,用于计算最小的长宽比 double qie(int x, int y, int n) { if (x < y) swap(x, y); // 保证x是较长边 int g = gcd(x, y); if (g != 1) { x /= g; y /= g; } if (n == 1) return static_cast<double>(x) / y; // 终止条件 double ans = 10000000; for (int i = 1; i < n; ++i) { // 横向切分 ans = min(ans, max(qie(x * i, y * n, i), qie(x * (n - i), y * n, n - i))); // 纵向切分 ans = min(ans, max(qie(x * n, y * i, i), qie(x * n, y * (n - i), n - i))); } return ans; } int main() { scanf("%d%d%d", &x, &y, &n); printf("%.6lf\n", qie(x, y, n)); return 0; } ``` ### 代码解析 1. **gcd函数**:用于化简长宽比,避免浮点数计算误差。 2. **qie函数**: - 首先交换长宽,确保长边在前。 - 化简长宽比的分数,避免重复计算。 - 递归终止条件:当只剩一块时,返回长宽比。 - 枚举所有可能的切分方式,取最小的长宽比。 3. **main函数**:读取输入并调用递归函数,输出结果。 ### 复杂度分析 - **时间复杂度**:由于每次递归会枚举所有可能的切分方式,时间复杂度为指数级,但由于数据范围较小,可以通过递归直接解决。 - **空间复杂度**:递归深度由切分次数决定,空间复杂度较低。 ### 总结 这道题通过递归的方式,将大问题分解为子问题,结合枚举所有可能的切分方式,最终找到最优解。递归与分治策略是解决此类问题的核心思想。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值