Cerc2014 Pork barre

有N个点,M条无向虚边(边还未连接上去),Q组询问,询问格式如下: 
对于第一个询问输入2个数L1,H1,表示你要用满足权值L1<=Ai<=H1的所有边来连接点使得能互相通达的点的数量最多。在此条件下,输出最小的权值和C1. 
对于第i个询问(1<i<=q),输入2个数Li’,Hi’, Li = Li’-lastans, Hi = Hi’-lastans;(当i为1是lastans = 0)

 N<=1000, M<=100000, Q<=1000000),输入2个数Li’,Hi’,其中Li=li’+c(i-1)

 

题解

       双向链表写错了…线段树写成O(n)了,改蠢我了…

       这题直接说做法吧,首先把边从大到小排序,每次加一条边,然后如果形成了环,就把环上最大的边删去(最小生成树),这个因为n只有1000所以可以暴力找,否则需要动态树。假设加入的边边权为vi,当查询xi,vi时,答案就是生成树中边权为在xi…vi之间的边的和,用线段树就可以了,但是这题强制在线,所以用可持久化线段树

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
int Int(){
	char ch; int tmp = 0; 
    while (ch = getchar()) if (ch >= '0' && ch <= '9') break;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) tmp = tmp * 10 + ch - '0';
	return tmp;
}
using namespace std;
const int maxn = 1100, maxm = 110000, maxw = 1000100;
int n, m;
struct Tedge{
	int x, y, v;
}edge[maxm];
void init(){
	n = Int(), m = Int();
	for (int i = 0; i < m; i ++)
		edge[i].x = Int(), edge[i].y = Int(), edge[i].v = Int();
}
int now[maxn], son[maxm * 2], pre[maxm * 2], v[maxm * 2];
bool cmp(Tedge a, Tedge b){ return a.v > b.v; }
int tot, next[maxm * 2];
inline void cc(int a, int b, int c){
	pre[++ tot] = now[a];next[now[a]] = tot; 
	next[tot] = 0;
	now[a] = tot; son[tot] = b; v[tot] = c;
}
inline void make(int a, int b, int c){cc(a, b, c); cc(b, a, 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];  
}
struct segment{
	struct node{
		int sum; node *ls, *rs;
		void clear(){ ls = rs = 0, sum = 0; }
		void updata(){ sum = (ls?ls->sum:0) + (rs?rs->sum:0); }
	}*root[maxw], t[maxw * 10];
	int tot;
	void build(node *&p, int l, int r){
		p = newnode(); int mid = (l + r) / 2;
		if (l == r) return; 
		build(p->ls, l, mid); build(p->rs, mid + 1, r);
	}
	void modify(node *last, node *&p, int l, int r, int x, int add){
		p = newnode(); *p = *last; 
		if (l == r){ p->sum += add; return; }
		int mid = (l + r) >> 1;
		if (x <= mid) modify(last->ls, p->ls, l, mid, x, add);
		else modify(last->rs, p->rs, mid + 1, r, x, add);
		p->updata();
	}
	void clear(){ 
		memset(root, 0, sizeof(root)); 
		tot = 0; build(root[edge[0].v + 1], 1, edge[0].v);
	}
	node *newnode(){ t[++tot].clear(); return &t[tot]; }
	void modify(int x, int y, int add){
		if (root[x]){
			root[0] = newnode(), modify(root[x], root[0], 1, edge[0].v, y, add);
			root[x] = root[0];
		}
		else modify(root[x + 1], root[x], 1, edge[0].v, y, add);
	}
	int query(node *p, int l, int r, int x, int y){
//		cout <<l <<' ' <<r <<' ' <<x <<' ' <<y <<endl;
		if (p->sum == 0) return 0;
		if (l == x && r == y) return p->sum; 
		int mid = (l + r) >> 1;
		if (mid >= y) return query(p->ls, l, mid, x, y);
		else if (mid < x) return query(p->rs, mid + 1, r, x, y);
		return query(p->ls, l, mid, x, mid) + query(p->rs, mid + 1, r, mid + 1, y);
	}
	int query(int x, int a, int b){
		if (b > edge[0].v) b = edge[0].v;
		if (a > edge[0].v) return 0;
//		cout <<x <<' ' <<a <<' ' <<b <<endl;
		return query(root[a], 1, edge[0].v, a, b);
	}
}seg;
void find(int vv, int x, int y){
	static int h[maxn];
	int tt = 0, ww = 1; h[1] = x;
	static int frm[maxn]; memset(frm, 0, sizeof(frm));
	while (tt ++ != ww){
		for (int p = now[h[tt]]; p; p = pre[p])
			if (!frm[son[p]] && son[p] != x) h[++ ww] = son[p], frm[son[p]] = p;
		if (frm[y]) break;
	}
	if (!frm[y]) return; Tedge t; int maxv = 0;
	for (int i = y; i != x; i = son[frm[i] ^ 1])
		if (v[frm[i]] > maxv){
			t.x = son[frm[i] ^ 1], t.y = i; t.v = (frm[i] ^ 1);
			maxv = v[frm[i]];
		}
	del(t.x, t.v ^ 1), del(t.y, t.v); 
	seg.modify(vv, maxv, -maxv);
}
inline void write(int x){if (!x) return;write(x/10); putchar(x%10+'0');}
void work(){
	sort(edge, edge + m, cmp); tot = 1; seg.clear();
	memset(now, 0, sizeof(now));
//	memset(next, 0, sizeof(next));
	int j = 0;
	for (int i = 0; i < m; i = j){
		int sum = 0;
		for (j = i; edge[j].v == edge[i].v; j ++){
			int x = edge[j].x, y = edge[j].y;
			find(edge[j].v, x, y);
			make(edge[j].x, edge[j].y, edge[j].v);
			sum += edge[j].v;
		}
		seg.modify(edge[i].v, edge[i].v, sum);
		for (int k = edge[i].v - 1; k > edge[j].v; k --) seg.root[k] = seg.root[edge[i].v];
	}
	int q = Int(), last = 0;
	for (int i = 1; i <= q; i ++){
		int a = Int() - last, b = Int() - last;
		last = seg.query(b, a, b); 
		if (last) write(last); else putchar('0'); putchar('\n');
	}
}
int main(){
	int T = Int();
	while (T -- ) init(), work();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值