CSP-S 模拟 19/10/29

T1:一个数列,可以给一个数加 a a a,减 a a a,加 b b b,减 b b b,问最小次数
首先每个数是独立的 ,然后就是求 满足 a x + b y = c ax+by=c ax+by=c m i n ( ∣ x ∣ + ∣ y ∣ ) min(|x|+|y|) min(x+y)
不妨设 a < b a<b a<b,那么尽量多选 b b b 最优,于是 x x x 在最优情况下取的值一定是正负最接近 0 的
然后我就是 W A WA WA 掉了:
a x + b y = c + k ∗ l c m ( a , b ) ax+by=c+k*lcm(a,b) ax+by=c+klcm(a,b),都是有解的
然后 ( x , y ) (x,y) (x,y) 的通解是 ( x + k b g c d ( a , b ) , y − k a g c d ( a , b ) (x+k\frac{b}{gcd(a,b)},y-k\frac{a}{gcd(a,b}) (x+kgcd(a,b)b,ykgcd(a,ba)

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e5 + 5;
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;
ll a, b, ans, x, y;
int n;
ll exgcd(ll a, ll b){
	if(!b){ x = 1; y = 0; return a; }
	int g = exgcd(b, a % b); ll x1 = y, y1 = x - a/b * y;
	x = x1, y = y1; return g;
}
int main(){
	n = read(); a = read(); b = read();
	if(a > b) swap(a, b);
	for(int i = 1; i <= n; i++){
		ll c = read();
		int g = exgcd(a, b);
		if(c % g){ puts("-1"); return 0; }
		x = x * c / g;
		y = y * c / g;
		ll A = a / g, B = b / g;
		ll k = x / B; x -= k * B; y += k * A;
		ll sum = abs(x) + abs(y);
		if(x <= 0){ x += B; y -= A; }
		else{ x -= B; y += A; }
		sum = min(sum, abs(x) + abs(y));
		ans += sum;
	} cout << ans; return 0;
}

T2:有 n n n 个数对 ( a i , b i ) (a i ,b i ) (ai,bi),第 i i i 个数对的权值为 w i w_i wi
你需要选出一些数对并以任意顺序排列它们,满足对于所有 i < j i<j i<j ,都有 a i ≤ b j a_i\le b_j aibj
最大化你选出的数对的权值和

好题:
首先主要的难点在于重新排列:
考虑贪心,套路一般地讨论交换邻项的最优值
对于一个 ( i , j ) b i ≥ a j , b j < a i (i,j)b_i\ge a_j,b_j< a_i (i,j)biaj,bj<ai,那么 i i i 排在前面是更优的
对于一个 ( i , j ) b i < a j , b j ≥ a i (i,j)b_i<a_j,b_j\ge a_i (i,j)bi<aj,bjai 那么 j j j 放在前面是更优的
发现如果不满足上述条件,任意排列都没有影响
于是按 a i + b i a_i+b_i ai+bi 排序即可
也就是说最后的答案序列一定是满足 a i + b i a_i+b_i ai+bi 递增的时候最优
我们可以直接在这个排好序的序列上 d p dp dp
考虑到任意 i , j i,j i,j 都有 b j ≥ a i b_j\ge a_i bjai,也就是说当前的 b i ≥ m a x ( a j ) j ∈ [ 1 , i ) b_i\ge max(a_j)_{j\in[1,i)} bimax(aj)j[1,i)
f i , j f_{i,j} fi,j 表示到 i i i 最大值为 j j j 的最大值,发现每次都是从 ≤ b i \le b_i bi 的转移,想到了线段树优化 d p dp dp
滚动数组,如果当前不选,线段树不用修改
如果要选, f i , m a x ( j , a i ) = m a x ( f i − 1 , j ) + w i ( j ∈ [ 1 , b i ] ) f_{i,max(j,a_i)}=max(f_{i-1,j})+w_i(j\in[1,b_i]) fi,max(j,ai)=max(fi1,j)+wi(j[1,bi])
发现需要讨论:
a i ≥ b i a_i\ge b_i aibi,那么查 [ 1 , b i ] [1,b_i] [1,bi] m a x max max 更新到 a i a_i ai 即可
a i < b i a_i< b_i ai<bi,查 [ 1 , a i ] [1,a_i] [1,ai] m a x max max 更新到 a i a_i ai,对于 j ∈ ( a i , b i ] j\in(a_i,b_i] j(ai,bi] a i a_i ai 不会对 m a x max max 造成影响,区间加即可


#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e5 + 5;
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, c[N << 1], siz, id[N];
struct node{ int a, b, w; bool operator < (cs node &A) cs{ return a + b < A.a + A.b; } }x[N];
typedef long long ll;
struct segmentree{	
	ll mx[N << 3], tg[N << 3];
	#define mid ((l+r)>>1)
	void pushup(int x){ mx[x] = max(mx[x<<1], mx[x<<1|1]); }
	void pushnow(int x, ll v){ tg[x] += v; mx[x] += v; }
	void pushdown(int x){ if(tg[x]) pushnow(x<<1, tg[x]), pushnow(x<<1|1, tg[x]), tg[x] = 0; } 
	void modify(int x, int l, int r, int p, ll v){
		if(l == r){ mx[x] = max(mx[x], v); return; } 
		pushdown(x);
		if(p <= mid) modify(x<<1, l, mid, p, v);
		else modify(x<<1|1, mid+1, r, p, v);
		pushup(x); 
	}
	void add(int x, int l, int r, int L, int R, ll v){
		if(L<=l && r<=R){ pushnow(x, v); return; }
		pushdown(x);
		if(L<=mid) add(x<<1, l, mid, L, R, v);
		if(R>mid) add(x<<1|1, mid+1, r, L, R, v);
		pushup(x);
	}
	ll query(int x, int l, int r, int L, int R){
		if(L<=l && r<=R) return mx[x]; pushdown(x); ll ans = 0;
		if(L<=mid) ans = max(ans, query(x<<1, l, mid, L, R));
		if(R>mid) ans = max(ans, query(x<<1|1, mid+1, r, L, R));
		return ans;
	}
}seg;
int main(){
	n = read();
	for(int i = 1; i <= n; i++){
		x[i].a = c[++siz] = read(), x[i].b = c[++siz] = read(), x[i].w = read();
	} sort(c + 1, c + siz + 1); siz = unique(c + 1, c + siz + 1) - (c + 1);
	for(int i = 1; i <= n; i++){
		x[i].a = lower_bound(c + 1, c + siz + 1, x[i].a) - c;
		x[i].b = lower_bound(c + 1, c + siz + 1, x[i].b) - c;
	} sort(x + 1, x + n + 1);
	for(int i = 1; i <= n; i++){
		if(x[i].a >= x[i].b){
			ll f = seg.query(1, 1, siz, 1, x[i].b);
			seg.modify(1, 1, siz, x[i].a, f + x[i].w); // 单点修改 max 
		} 
		else{
			ll f = seg.query(1, 1, siz, 1, x[i].a);
			seg.modify(1, 1, siz, x[i].a, f + x[i].w);
			seg.add(1, 1, siz, x[i].a + 1, x[i].b, x[i].w);
		} 
	} cout << seg.mx[1]; return 0;
}

T3:给定一张 n n n 个点 m m m 条边的 带边权 连通 无向图 ,其中有 p p p 个点是特殊点 。
对于每个特殊点,求出它到离它最近的其它特殊点的距离
G X O I 2019 GXOI2019 GXOI2019 一样,每个特殊点一起作为起点对图染色,预处理一个点到哪个特殊点最近
然后考虑一条跨过染色块的边,用它来更新两边染色块的答案
正确性:对于两个特殊点的所有最短路,一定存在两个点属于不同的染色块,也就是说两个特殊点的最短路每一条都是讨论到了的

#include<bits/stdc++.h>
#define cs const
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;
cs int N = 2e5 + 5, M = 4e5 + 5;
int n, m, k;
int first[N], nxt[M], to[M], w[M], tot;
void add(int x, int y, int z){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
}
struct edge{ int u, v, w; }e[N];
#define mp make_pair
#define se second
typedef pair<ll, int> pi;
ll dis[N], ans[N]; bool vis[N]; int col[N];
int a[N];
void Dijsktra(){
	priority_queue<pi> q;
	memset(dis, 0x3f, sizeof(dis));
	for(int i = 1; i <= k; i++){
		int x = a[i]; 
		q.push(mp(0, x)); col[x] = x; dis[x] = 0;
	} 
	while(!q.empty()){
		int x = q.top().se; q.pop();
		if(vis[x]) continue; vis[x] = true;
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; if(dis[t] > dis[x] + w[i]){
				dis[t] = dis[x] + w[i]; col[t] = col[x];
				q.push(mp(-dis[t], t));
			}
		}
	}
}
int main(){
	n = read(); m = read(); k = read();
	for(int i = 1; i <= k; i++) a[i] = read();
	for(int i = 1; i <= m; i++){
		int x = read(), y = read(), z = read();
		add(x, y, z); add(y, x, z);
		e[i] = (edge){x, y, z};
	} Dijsktra();
	memset(ans, 0x3f, sizeof(ans));
	for(int i = 1; i <= m; i++){
		if(col[e[i].u] == col[e[i].v]) continue;
		ll d = dis[e[i].u] + dis[e[i].v] + e[i].w;
		ans[col[e[i].u]] = min(ans[col[e[i].u]], d);
		ans[col[e[i].v]] = min(ans[col[e[i].v]], d);
	} 
	for(int i = 1; i <= k; i++) cout << ans[a[i]] << " ";
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FSYo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值