省选模拟 19/10/03

T1:原点向每个点连流量为 1 的边,表示出度最多为 1,每个点的对应点向汇点连流量为 1 的边,表示入度最多为 1,点与点之间对应连边
发现一个点不选可能反而更优,于是有一种做法就是每个点再向汇点连一条流量为 0 的边表示不选这个点
但也可以直接最小费用可行流

#include<bits/stdc++.h>
#define N 100050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int first[N], nxt[N], to[N], w[N], c[N], tot = 1;
void add(int x, int y, int z, int v){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z, c[tot] = v;
	nxt[++tot] = first[y], first[y] = tot, to[tot] = x, w[tot] = 0, c[tot] = -v;
}
int n, m, st, ed;
typedef long long ll;
ll dis[N]; bool vis[N];
int from[N], froms[N];
const ll inf = 1e15;
bool spfa(){
	queue<int> q; q.push(st);
	for(int i = 0; i <= ed; i++) dis[i] = inf, vis[i] = 0;
	dis[st] = 0; 
	while(!q.empty()){
		int x = q.front(); q.pop(); vis[x] = 0;
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; if(w[i] && dis[t] > dis[x] + (ll)c[i]){
				from[t] = x; froms[t] = i;
				dis[t] = dis[x] + (ll)c[i]; if(!vis[t]) q.push(t), vis[t] = 1;
			}
		}
	} return dis[ed] != inf;
}
int calc(){
	int flow = 0x3fffffff, u = ed;
	while(u != st) flow = min(flow, w[froms[u]]), u = from[u]; u = ed;
	while(u != st) w[froms[u]] -= flow, w[froms[u] ^ 1] += flow, u = from[u];
	return flow;
}
ll dinic(){
	ll ans = 0; while(spfa()){
		if(dis[ed] >= 0) break;
		ans += 1ll * dis[ed] * calc();
	} return ans;
}
int main(){
	n = read(), m = read(); st = 0; ed = n + n + 1;
	ll sum = 0;
	for(int i = 1; i <= m; i++){
		int x = read(), y = read(), z = read();
		sum += (ll)z;
		add(x, y + n, 1, -max(z, 0));
	}
	for(int i = 1; i <= n; i++) 
		add(st, i, 1, 0), add(i + n, ed, 1, 0);
	cout << sum + dinic(); return 0;
}

T2:首先最多被染色 n + m − 1 n + m-1 n+m1
然后就可以强制一行或一列不染色,得到先后顺序的关系然后拓扑排序判断
先后顺序的讨论如下:
首先不选的那一行的颜色一定来自其它列,因此我们能就确定列的颜色
对于每一个列:枚举它的所有点,如果不等于事先钦定的颜色,那么这个颜色来自于行
于是列要先于行选,然后钦定这一行的颜色为它,如果跟其它列钦定的冲突就不合法
如果扫到一个点与这一行钦定的不同但是与这一列钦定的相同也是合法的,这是需要强制行先于列选
对于每一个行也是同理

#include<bits/stdc++.h>
#define N 55
#define M 500050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, m, c, mp[N][N];
int first[N << 1], nxt[M], to[M], tot, du[M];
void add(int x, int y){ ++du[y]; nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
struct Node{ int id, col;};
vector<Node> ans;
int vis[N << 1], sta[N << 1], top, ban[N << 1];
bool topsort(){
	queue<int> q; for(int i = 1; i <= n + m; i++) if(!du[i]) q.push(i);
	top = 0;
	while(!q.empty()){
		int x = q.front(); q.pop(); sta[++top] = x; 
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; if(--du[t] == 0) q.push(t);
		}
	} return top == n + m;
}
void calc(int k){
	memset(first, 0, sizeof(first)); tot = 0;
	memset(du, 0, sizeof(du));
	memset(vis, -1, sizeof(vis)); 
	if(k <= n){
		for(int i = 1; i <= m; i++) vis[i + n] = mp[k][i];
		for(int i = 1; i <= n; i++){
			if(i == k) continue;
			for(int j = 1; j <= m; j++){
				if(vis[j + n] != mp[i][j]){
					if(mp[i][j] != vis[i] && vis[i] != -1) return;
					vis[i] = mp[i][j]; if(vis[i] == 0) return;
					add(j + n, i);
				}
			}
			for(int j = 1; j <= m; j++)
				if(vis[j + n] == mp[i][j])
					if(vis[i] != mp[i][j]) add(i, j + n);
		}
	}
	else{
		for(int i = 1; i <= n; i++) vis[i] = mp[i][k - n];
		for(int i = 1; i <= m; i++){
			if(i + n == k) continue;
			for(int j = 1; j <= n; j++){
				if(vis[j] != mp[j][i]){
					if(mp[j][i] != vis[i + n] && vis[i + n] != -1) return;
					vis[i + n] = mp[j][i]; if(vis[i + n] == 0) return;
					add(j, i + n);
				}
			}
			for(int j = 1; j <= n; j++)
				if(vis[j] == mp[j][i])
					if(vis[i + n] != mp[j][i]) add(i + n, j);
		}
	}
	for(int i = 1; i <= n + m; i++) if(ban[i] && vis[i] != -1 && vis[i] != 0) return;
	if(topsort()){
		vector<Node> now;
		for(int i = 1; i <= n + m; i++){
			if(vis[sta[i]] != -1 && vis[sta[i]]) now.push_back((Node){sta[i], vis[sta[i]]});
		} if(now.size() < ans.size() || ans.empty()) ans = now;
	}
}
int main(){
	n = read(), m = read(), c = read();
	for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) mp[i][j] = read(), ban[i] |= !mp[i][j], ban[j + n] |= !mp[i][j];
	for(int i = 1; i <= n + m; i++) calc(i);
	if(ans.empty()){ cout << "-1"; return 0;}
	cout << ans.size() << '\n';
	for(int i = 0; i < ans.size(); i++){
		if(ans[i].id <= n) cout << "R ";
		else ans[i].id -= n, cout << "C ";
		cout << ans[i].id << " " << ans[i].col << '\n';
	} return 0;
} 

T3:开始想的是对于整个时间二分,然后从叶子开始看能跳到哪?但这样做有个坏处就是不知道每个点的解放时间,对于除叶子以外的点就没法算贡献
然后发现可以二分每一个点的解放时间,而它的解放时间只跟子树内的点有关
二分一个时间,子树内一个解放的点对它的贡献就是
T i m e − t i m e u − d i s = T i m e + d i s n o w − ( t i m e u + d i s u ) Time-time_u-dis=Time+dis_{now}-(time_u+dis_u) Timetimeudis=Time+disnow(timeu+disu)
然后把后面的插入平衡树,启发式合并就解决了

#include<bits/stdc++.h>
#define N 200050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
typedef long long ll;
int Rnd(){ return (rand() << 15) | rand();}
int first[N], nxt[N], to[N], w[N], tot;
void add(int x, int y, int z){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
}
ll dis[N];
int n, d[N]; ll tim[N], val, ans;

int id[N], rt[N], node;
struct Node{ int ch[2]; ll siz, val, sum, num; int rnd;}t[N << 3];
#define ls t[x].ch[0]
#define rs t[x].ch[1]
stack<int> bin;
int newnode(ll val){ 
	int x = 0; if(bin.size()) x = bin.top(), bin.pop(); else x = ++node;
	ls = rs = 0; t[x].siz = t[x].num = 1; t[x].val = t[x].sum = val; 
	t[x].rnd = Rnd(); return x;
}
void pushup(int x){ t[x].siz = t[ls].siz + t[rs].siz + t[x].num; t[x].sum = t[x].num * t[x].val + t[ls].sum + t[rs].sum;}
void Lrot(int &x){int y = ls; t[x].ch[0] = t[y].ch[1]; t[y].ch[1] = x; t[y].sum = t[x].sum; t[y].siz = t[x].siz; pushup(x); x = y;}
void Rrot(int &x){int y = rs; t[x].ch[1] = t[y].ch[0]; t[y].ch[0] = x; t[y].sum = t[x].sum; t[y].siz = t[x].siz; pushup(x); x = y;}
void ins(int &x, ll val){
	if(!x){ x = newnode(val); return; } t[x].sum += val; ++t[x].siz;
	if(val == t[x].val){ ++t[x].num; return;}
	if(val < t[x].val){ ins(ls, val); if(t[x].rnd > t[ls].rnd) Lrot(x);}
	else { ins(rs, val); if(t[x].rnd > t[rs].rnd) Rrot(x); }
}
stack<ll> sta;
void pia(int x){ if(!x) return; if(ls) pia(ls); bin.push(x); sta.push(t[x].val); if(rs) pia(rs);}
ll query(int x, ll val){
	if(!x) return 0; 
	if(val < t[x].val) return query(ls, val);
	else if(val == t[x].val) return val * t[ls].siz - t[ls].sum;
	else return val * (t[ls].siz + t[x].num) - (t[ls].sum + t[x].val * t[x].num) + query(rs, val);
}
void dfs(int u, int f){
	int flg = 0;
	for(int i = first[u]; i; i = nxt[i]){
		int v = to[i]; if(v == f) continue;
		flg = 1; dis[v] = dis[u] + w[i]; dfs(v, u);
		if(t[rt[id[u]]].siz < t[rt[id[v]]].siz) swap(id[u], id[v]);
		pia(rt[id[v]]);
		while(!sta.empty()) ins(rt[id[u]], sta.top()), sta.pop();
	} 
	if(!flg){ tim[u] = 0; ins(rt[id[u]], dis[u]); } 
	else{
		int l = 0, r = 1e8;
		while(l < r){
			int mid = (l+r) >> 1; 
			val = query(rt[id[u]], mid + dis[u]);
			if(val >= d[u]) r = mid; else l = mid + 1; 
		} tim[u] = l; ans = max(ans, tim[u]);
		ins(rt[id[u]], dis[u] + tim[u]);
	}
}
int main(){
	srand(time(0));
	freopen("conquer.in","r",stdin);
	freopen("conquer.out","w",stdout);
	memset(tim, 63, sizeof(tim));
	n = read();
	for(int i = 1; i <= n; i++) d[i] = read(), id[i] = i;
	for(int i = 1; i < n; i++){
		int x = read(), y = read(), z = read();
		add(x, y, z); add(y, x, z);
	} dfs(1, 0); 
	cout << ans; return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值