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+k∗lcm(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,y−kgcd(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
ai≤bj
最大化你选出的数对的权值和
好题:
首先主要的难点在于重新排列:
考虑贪心,套路一般地讨论交换邻项的最优值
对于一个
(
i
,
j
)
b
i
≥
a
j
,
b
j
<
a
i
(i,j)b_i\ge a_j,b_j< a_i
(i,j)bi≥aj,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,bj≥ai 那么
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
bj≥ai,也就是说当前的
b
i
≥
m
a
x
(
a
j
)
j
∈
[
1
,
i
)
b_i\ge max(a_j)_{j\in[1,i)}
bi≥max(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(fi−1,j)+wi(j∈[1,bi])
发现需要讨论:
a
i
≥
b
i
a_i\ge b_i
ai≥bi,那么查
[
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;
}