CSP-S 模拟 19/10/22

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 hix 的个数
边数是 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,b1,b+1,c1 a + 1 , c − 1 a+1,c-1 a+1,c1 是等价的
于是我们可以将两条边缩成一条边,对这一条边定向,就可以确定两条边的方向
于是对于 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

分类讨论:

  1. ( x , y ) (x,y) (x,y) 为重边,如果之前是 ( y , x ) (y,x) (y,x),那么两个同向,均赋成 1 即可,并且在只后的讨论中忽略这两条边
  2. 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 的相同,否则相反
  3. y y y 上连了一个 z z z x x x y y y 连边,等价于 x x x 直接向 z z z 连边
    同上
  4. ( 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FSYo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值