spongebob
显然最小值只会在 0 点取到,于是按 0 点排序,两边前缀和统计即可
#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;
}
cs int N = 3e5 + 5;
typedef long long ll;
ll A[N], B[N], sumA[N], sumB[N], sufA[N], sufB[N];
typedef long double ld;
int n, id[N]; ld X[N];
bool cmp(int a, int b){ return X[a] < X[b]; }
int main(){
n = read();
for(int i = 1; i <= n; i++){
int a = read(), b = read();
id[i] = i;
if(a < 0) a = -a, b = -b;
A[i] = a; B[i] = b;
X[i] = - (ld)B[i] / A[i];
} sort(id + 1, id + n + 1, cmp);
for(int i = 1; i <= n; i++) sumA[i] = sumA[i-1] + A[id[i]], sumB[i] = sumB[i-1] + B[id[i]];
for(int i = n; i >= 1; i--) sufA[i] = sufA[i+1] - A[id[i]], sufB[i] = sufB[i+1] - B[id[i]];
ld ans = 1e18;
for(int i = 1; i <= n; i++){
int nx = id[i];
ld ret = sumA[i] * X[nx] + sumB[i] + sufA[i] * X[nx] + sufB[i];
if(ret < ans) ans = ret;
} printf("%.6lf", (double)ans);
}
patrick
考虑 连通块个数 = 点数 - 边数
分别维护点数和边数,点数是
h
i
≥
x
h_i\ge x
hi≥x 的个数
边数是
m
i
n
(
h
i
,
h
i
+
1
)
≥
x
min(h_i,h_{i+1})\ge x
min(hi,hi+1)≥x 的个数
两个树状数组即可
#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;
}
cs int N = 5e5 + 5, mx = 5e5;
int n, m, h[N], lim[N], ans;
struct BIT{
int c[N];
void add(int x, int v){ for(;x<=mx;x+=x&-x) c[x] += v; }
int ask(int x){ int ans = 0; for(;x;x-=x&-x) ans += c[x]; return ans;}
}T[2];
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++) h[i] = read(), T[1].add(h[i], 1);
for(int i = 1; i < n; i++){
lim[i] = min(h[i], h[i + 1]), T[0].add(lim[i], 1);
}
while(m--){
char op[3]; scanf("%s", op);
if(op[0] == 'Q'){
int x = read() ^ ans;
int node = n - T[1].ask(x - 1);
int edge = n - 1 - T[0].ask(x - 1);
ans = node - edge;
cout << ans << '\n';
}
if(op[0] == 'C'){
int p = read() ^ ans, x = read() ^ ans;
T[1].add(h[p], -1);
h[p] = x;
T[1].add(h[p], 1);
if(p > 1){
T[0].add(lim[p - 1], -1);
lim[p - 1] = min(h[p - 1], h[p]);
T[0].add(lim[p - 1], 1);
}
if(p < n){
T[0].add(lim[p], -1);
lim[p] = min(h[p], h[p + 1]);
T[0].add(lim[p], 1);
}
}
} return 0;
}
eugene
考场写了一个欧拉回路的做法,拿了
70
p
t
s
70 pts
70pts
对于
c
i
=
1
c_i=1
ci=1 的情况,每个点的度数都为奇数
考虑建一个虚点, 每个点向虚点连边,跑欧拉回路,因为每个点连出去的不在原图中的只有到虚点的一条边,这样就可以保证每个点的出度入度差为
1
,
−
1
1,-1
1,−1
正解:
考虑到如果存在
(
a
,
b
)
,
(
b
,
c
)
(a,b),(b,c)
(a,b),(b,c) 两条边,如果
a
+
1
,
b
−
1
,
b
+
1
,
c
−
1
a+1,b-1,b+1,c-1
a+1,b−1,b+1,c−1 与
a
+
1
,
c
−
1
a+1,c-1
a+1,c−1 是等价的
于是我们可以将两条边缩成一条边,对这一条边定向,就可以确定两条边的方向
于是对于
c
i
=
1
c_i = 1
ci=1,最后一定可以缩成
n
2
\frac{n}{2}
2n 条边
随便定向再扩展回去即可
对于
c
i
=
2
c_i=2
ci=2,一个点最多只有一个出边为
1
1
1,最多只有一个出边为
2
2
2,所以最后的图是一堆 1,2 边交错出现的环,或者交错出现的链组成的
强制 1 出 2 进 即可
如何缩边:
为了处理方便,我们在加边的时候动态缩
对于一条新的边
(
x
,
y
)
(x,y)
(x,y),
x
x
x 上可能有边,
y
y
y 上可能有边
规定:
定向,x 指向 y,表示顾客评了 y,y 的度数加,此时输出 1
分类讨论:
- ( x , y ) (x,y) (x,y) 为重边,如果之前是 ( y , x ) (y,x) (y,x),那么两个同向,均赋成 1 即可,并且在只后的讨论中忽略这两条边
-
x
x
x 上连了一个
z
z
z,
x
x
x 向
y
y
y 连边,等价于
z
z
z 向
y
y
y 连边
考虑如何记录扩展回去的答案,继续分类讨论
如果方向为 z 向 y,那么原边方向为 z 到 x,x 到 y
如果原来是 x 指向 z,那么答案与 z 向 y 的相同,否则相反 -
y
y
y 上连了一个
z
z
z,
x
x
x向
y
y
y 连边,等价于
x
x
x 直接向
z
z
z 连边
同上 - ( x , u ) , ( y , v ) (x,u),(y,v) (x,u),(y,v) 有边,先用 ( u , y ) (u,y) (u,y)缩边,再用 ( u , v ) (u,v) (u,v) 缩边即可
通过等价条件极大规模缩小了边的个数,比较妙的想法!
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e6 + 5, M = N << 1;
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, tot;
struct edge{
int id, to;
}e[2][N];
int dir[2][N]; // 钦定方向
int depend[M]; // 依赖的边
int ans[M], rev[M]; // rev 维护相对关系
bool vis[N];
void add(int x, int y, int id, int type){
edge *now = e[type];
// 重边
int *d = dir[type];
if(now[x].to == y && now[x].id){
depend[now[x].id] = depend[id] = 0;
ans[id] = 1; ans[now[x].id] = d[y];
now[x].id = now[y].id = 0;
return;
}
bool flg = true;
++tot;
if(now[x].id){
flg = false;
depend[now[x].id] = tot;
rev[now[x].id] = d[x];
now[x].id = 0;
x = now[x].to;
}
if(now[y].id){
flg = false;
depend[now[y].id] = tot;
rev[now[y].id] = d[y] ^ 1;
now[y].id = 0;
y = now[y].to;
} now[x].to = y; now[y].to = x;
d[x] = 1; d[y] = 0;
if(flg) now[x].id = now[y].id = id;
else depend[id] = now[x].id = now[y].id = tot;
}
void work(int u){
int x = u, typ = 0;
vis[x] = 1;
while(e[typ][x].id){
ans[e[typ][x].id] = dir[typ][x];
if(vis[e[typ][x].to]) return;
vis[e[typ][x].to] = true;
x = e[typ][x].to; typ ^= 1;
}
x = u; typ = 1;
while(e[typ][x].id){
ans[e[typ][x].id] = dir[typ][x] ^ 1;
vis[e[typ][x].to] = true;
x = e[typ][x].to; typ ^= 1;
}
}
int main(){
n = read(), m = tot = read();
for(int i = 1; i <= m; i++){
int x = read() + 1, y = read() + 1, z = read();
add(x, y, i, z - 1);
} for(int i = 1; i <= n; i++) if(!vis[i]) work(i);
for(int i = tot; i >= 1; i--) if(depend[i]) ans[i] = ans[depend[i]] ^ rev[i];
for(int i = 1; i <= m; i++) cout << ans[i];
return 0;
}