标签:10 Latin return 剖分 Contest int pos Maxn include
题意:给N个点,M条边的图,M >= N-1 , 每次给个特殊边 , 问必须用这个边的最小生成树 的边权和
(两个点间没有重边)
树链剖分裸题 , 边权转点权
在DFS两边的过程中 , 把边权 转到 它对应子节点 的点上。
树链剖分维护 U , V 这两个点中间 边权最大值
注意WA点 , 我们用map 标记一条边 的时候 , U , V中间用个特殊符号隔开 , 不然会出现
类似 131,14444 和 1311 , 4444 这种情况 , 导致map标记错误,一直wa3
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
using namespace std;
typedef long long LL;
typedef pair <int , int> PLL;
const int Maxn = 4e5 + 10;
const int Inf = 1e9 + 7;
int N , M , cnt , root;
int Mod;
int pos[Maxn] , top[Maxn] , sz[Maxn] , dep[Maxn] , fa[Maxn] , wson[Maxn];
int weigt[Maxn];
vector <PLL> G[Maxn<<2];
struct node{
int u , v;
LL w;
}B[Maxn];
int father[Maxn];
bool cmp(node a , node b){
return a.w < b.w;
}
//线段树部分
int A[Maxn];
struct edge{
int l , r;
int Max;
}tree[Maxn<<2];
void PushUp(int x){
tree[x].Max = max(tree[x<<1].Max , tree[x<<1|1].Max);
}
void Build(int l , int r , int x){
tree[x].l = l , tree[x].r = r;
if( l == r ){
tree[x].Max = A[l];
return;
}
int mid = ( l + r ) >> 1;
Build( l , mid , x << 1 );
Build( mid + 1 , r , x<<1|1 );
PushUp(x);
}
int Query(int L , int R , int x){
if(L > R) return 0; //L节点的深度 比 R节点大时 , 我们不进行查询返回0
if( L <= tree[x].l && tree[x].r <= R ){
return tree[x].Max;
}
int mid = ( tree[x].l + tree[x].r ) >> 1;
int res = 0;
if( L <= mid ) res = max(res , Query( L , R , x<<1 ));
if( R > mid ) res = max(res , Query( L , R , x<<1| 1 ));
return res;
}
//树链剖分部分
/*
* 第一遍dfs1
* 计算出每个的sz ----- 每个点的子树(包括自己)的的节点个数
* 计算出每个点的深度 dep
* 计算出每个点的 fa 父亲节点
* 计算出每个节点到 重儿子 所用的边权值 ,weigt[x]
* 接口 : x 是根节点 , fat是根节点的父亲节点 , dept是根节点的深度(1)
*/
void dfs1(int x , int fat , int dept){
dep[x] = dept , fa[x] = fat , sz[x] = 1;
int Size = G[x].size() , v , w;
for(int i = 0 ; i < Size ; i++){
v = G[x][i].first , w = G[x][i].second;
if(v == fat) continue;
dfs1(v , x , dept+1);
sz[x] += sz[v];
if(wson[x] == -1 || sz[wson[x]] < sz[v]) wson[x] = v , weigt[x] = w;
}
}
/*
* 第二遍dfs2
* 计算出每个点所在重链(或者轻链)的链头结点 top[x]
* 计算出每条边在序列中的位置 pos[x] (x到其父亲的边在序列中的位置 , 所有边组成了一个序列)
* 边权转点权 标记点
* 接口:x 当前节点 , line_top 链头 , dw 从父亲节点 到 当前节点 经过的边 的边权
*/
void dfs2(int x , int line_top , int dw){
top[x] = line_top, pos[x] = ++cnt;
A[pos[x]] = dw;
if(wson[x] == -1) return;
dfs2(wson[x] , line_top , weigt[x]);
int Size = G[x].size() , v , w;
for(int i = 0 ; i < Size ; i++){
v = G[x][i].first , w = G[x][i].second;
if(v != fa[x] && v != wson[x]) dfs2(v , v , w);
}
}
int query1(int u , int v){
int ans = 0;
while(top[u] != top[v]){
if(dep[top[u]] > dep[top[v]]) swap(u,v);
ans = max(ans , Query(pos[top[v]] , pos[v] , 1));
v = fa[top[v]];
}
if(dep[u] > dep[v]) swap(u,v);
ans = max(ans , Query(pos[wson[u]] , pos[v] , 1));
//查询时注意LCA节点的点权不能算在内 , 因为LCA节点的点权 不在路径上
return ans;
}
int Find(int x){
if(father[x] == x) return x;
else return father[x] = Find(father[x]);
}
void Union(int x , int y){
int fx = Find(x) , fy = Find(y);
if(fx == fy) return;
father[fx] = fy;
}
map < string , LL > mp;
string remake(int x , int y){
string ss="";
while(x){
ss += (char)((x%10)+'0');
x /= 10;
}
ss+='|'; //非常重要的分割线
while(y){
ss += (char)((y%10)+'0');
y /= 10;
}
return ss+'\0';
}
int main()
{
LL SUM = 0LL;
cnt = 0;
root = 1;
memset(wson , -1 , sizeof(wson));
scanf(" %d %d",&N,&M);
for(int i = 1 ; i <= N ; i++) father[i] = i;
for(int i = 1 ; i <= M ; i++){
scanf(" %d %d %lld",&B[i].u,&B[i].v,&B[i].w);
int u = B[i].u , v = B[i].v;
mp[remake(u , v)] = B[i].w;
}
sort(B + 1 , B + M + 1 , cmp);
int xt = 0;
for(int i = 1 ; i <= M ; i++){
int u = B[i].u , v = B[i].v;
LL w = B[i].w;
if(Find(u) != Find(v)){
Union(u , v);
G[u].push_back(PLL(v , (int)w));
G[v].push_back(PLL(u , (int)w));
SUM += w;
}
}
dfs1(root , -1 , 1);
dfs2(root , root , 0);
Build(1 , N , 1);
int Q , x , y;
LL z;
scanf(" %d",&Q);
while(Q--){
scanf(" %d %d",&x , &y);
int dx = query1(x , y);
z = mp[remake(x , y)];
LL ans = SUM - (LL)dx + z;
printf("%lld\n",ans);
}
return 0;
}
标签:10,Latin,return,剖分,Contest,int,pos,Maxn,include 来源: https://blog.csdn.net/castomere/article/details/90732412
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。