Cerc2014 Pork barre

题目大意:给你n个点m条边,再给你q个询问,查询边权在l~r之中,且覆盖点最多的边权和最小是多少 n<=1000 m<=100000 q<=1000000 w<=1000000  w是边权  强制在线


这题比较神,看了题想了一个小时都还没有思路,最后还是看了一下题解才弄懂


本质就是求边权在l~r内的最小生成树

先把所有的边按照边权从大到小排序,然后每次把边权相同的插入图中维护好最小生成树,再用一个可持久化线段树维护权值就OK了


#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1011,maxm=100011,maxw=1000000;
inline void read(int &x){
	char ch=getchar();x=0;
	while (!isdigit(ch)) ch=getchar();
	for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}
struct Tedge{int a,b,c;}e[maxm];
int n,m,N;
void init(){
	read(n); read(m);
	for (int i=1;i<=m;++i) read(e[i].a),read(e[i].b),read(e[i].c);
}
struct Tsegt{
	static const int maxn=10000011;
	struct node{
		node *lc,*rc; int s;
		void clear(){lc=rc=0; s=0;}
		void update(){int ls=lc?lc->s:0,rs=rc?rc->s:0; s=ls+rs;}
	}*root[maxw*2+11],e[maxn];
	int tot;
	node *newnode(){e[tot].clear(); return e+(tot++);}
	void build(node *p,int l,int r){
		if (l==r) return; int mid=(l+r)>>1;
		build(p->lc=newnode(),l,mid); build(p->rc=newnode(),mid+1,r);
	}
	void clear(){
		tot=0; memset(root,0,sizeof(root)); root[N+1]=newnode(); build(root[N+1],1,N);
	}
	void ins(node *lp,node *p,int l,int r,int m,int add){
		if (l==r && l==m) return p->s+=add*m,void();
		int mid=(l+r)>>1; if (lp->lc) p->lc=lp->lc; if (lp->rc) p->rc=lp->rc;
		if (m<=mid){
			p->lc=newnode(); if (lp->lc) *p->lc=*lp->lc;
			ins(lp->lc,p->lc,l,mid,m,add);
		}else{
			p->rc=newnode(); if (lp->rc) *p->rc=*lp->rc;
			ins(lp->rc,p->rc,mid+1,r,m,add);
		}
		p->update();
	}
	void ins(int w,int m,int add){
		if (!m) return;
		if (root[w]) root[0]=newnode(),ins(root[w],root[0],1,N,m,add),root[w]=root[0];
		else ins(root[w+1],root[w]=newnode(),1,N,m,add);
	}
	int Query(node *p,int l,int r,int fir,int las){
//		if (!p) return 0;
		if (l==fir && r==las) return p->s; int mid=(l+r)>>1;
		if (las<=mid) return Query(p->lc,l,mid,fir,las);
		if (fir>mid) return Query(p->rc,mid+1,r,fir,las);
		return Query(p->lc,l,mid,fir,mid)+Query(p->rc,mid+1,r,mid+1,las);
	}
	int Query(int a,int b){if (b>N) b=N; return Query(root[a],1,N,a,b);}
}t;
int pre[maxm<<1],next[maxm<<1],son[maxm<<1],v[maxm<<1],now[maxn],tot=1;
inline void add(int a,int b,int c){
	pre[++tot]=now[a]; next[now[a]]=tot; now[a]=tot; son[tot]=b; v[tot]=c;
}
inline void del(int x,int p){
	if (now[x]==p) now[x]=pre[p];
	next[pre[p]]=next[p]; pre[next[p]]=pre[p];
}
inline void cc(int a,int b,int c){add(a,b,c); add(b,a,c);}
void clear(){tot=1; v[0]=0; memset(now,0,sizeof(now)); memset(next,0,sizeof(next));}
int find(Tedge e){
	static int fp[maxn],fe[maxn],q[maxn];
	int t=0,w=1; q[1]=e.a; memset(fp,0,sizeof(fp)); fp[e.a]=-1;
	while (t++!=w) for (int p=now[q[t]];p;p=pre[p]) 
		if (!fp[son[p]]) fp[q[++w]=son[p]]=q[t],fe[son[p]]=p;
	Tedge ans; ans.c=0;
	for (int x=e.b;fp[x]>0;x=fp[x])
		if (v[fe[x]]>v[ans.c]) ans.a=fp[x],ans.b=x,ans.c=fe[x];
	if (ans.c) del(ans.a,ans.c),del(ans.b,ans.c^1); cc(e.a,e.b,e.c); return v[ans.c];
}
inline bool cmp(Tedge a,Tedge b){return a.c>b.c;}
void prepare(){
	sort(e+1,e+m+1,cmp); N=e[1].c; t.clear(); clear();
	for (int i=N;i>e[1].c;--i) t.root[i]=t.root[i+1];
	for (int i=1,j;i<=m;i=j){
		for (j=i;e[j].c==e[i].c;++j){
			t.ins(e[i].c,e[i].c,1);
			t.ins(e[i].c,find(e[j]),-1);
		}
		if (j<=m) for (int k=e[i].c-1;k>e[j].c;--k) t.root[k]=t.root[e[i].c];
	}
	for (int i=e[m].c-1;i>0;--i) t.root[i]=t.root[e[m].c];
}
void work(){
	prepare(); int q,a,b,la=0;
	read(q); while (q--){
		read(a); read(b); 
		a-=la; b-=la;
		printf("%d\n",la=t.Query(a,b));
	}
}
int main(){
	int T; read(T); while (T--) init(),work();
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值