CSP-S 模拟 19/10/05

T1:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
先前缀和一波,题目转换为两个人,每次选一个数做为自己的分数,每次都只能在对方后面选
冷静分析一下(假装自己在玩游戏)
首先,如果我选了最后一个,游戏结束,相差 S n S_n Sn
如果我选了倒数第二个,那么他只能选最后,相差 S n − 1 − S n S_{n-1}-S_n Sn1Sn
如果我选了倒数第三个,那么他以他的最优策略一定会选倒二和最后的最大的那个
于是倒过来处理一个后缀最大 m x i mx_i mxi
m x i = m a x ( m x i + 1 , S i − m x i + 1 ) mx_i=max(mx_{i+1},S_i-mx_{i+1}) mxi=max(mxi+1,Simxi+1)


T2:CF1053E Euler tour
首先看什么是不合法的:
1 和 2 ∗ n − 1 2*n-1 2n1 不等
存在 a < b < c < d a<b<c<d a<b<c<d使得 a a = a c , a b = a d a_a=a_c,a_b=a_d aa=ac,ab=ad,即相交
另外还有一些性质:
任意两个相等的数的间距为偶数,因为走下去走上来每条边要贡献两个点
我们可以假设当前处理 [ l , r ] [l,r] [l,r],先扫一遍有没有相等的数,如果有,那么中间的是一棵子树,递归处理,然后把它缩成一个点
现在可以发现,区间内不存在两个相同的数
假设共有 s u m sum sum个空位,有 c n t cnt cnt 个非0,如果 c n t > s u m + 1 2 cnt>\frac{sum+1}{2} cnt>2sum+1显然填不下,不合法
然后就可以强行把 0 的位置填上没有填的数,填满 s u m + 1 2 \frac{sum+1}{2} 2sum+1
然后处理剩下的 0,对于 x y 0 xy0 xy0,我们将 0 填成 x,然后缩成一个点
对于 0 y x 0yx 0yx,我们将 0 填成 x,然后缩成一个点
最后还剩下几个 0,就一定是这个区间子树的根了,填上即可

#include<bits/stdc++.h>
#define N 1000050
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;
}
void fail(){puts("zxn is a silly cat."); exit(0);}
int n, m, a[N], pre[N], suf[N], vis[N], nxt[N];
void del(int l, int r){ suf[pre[l]] = suf[r]; pre[suf[r]] = pre[l];}
int node = 1; 
int newnode(){ while(vis[node]) ++node; if(node > n) fail(); vis[node] = -1; return node;}
void Solve(int l, int r){
	if((r - l) & 1) fail();
	for(int i = l; i <= r; i = suf[i]){
		while(nxt[i]){
			if(nxt[i] > r) fail();
			Solve(suf[i], pre[nxt[i]]);
			del(suf[i], nxt[i]);
			nxt[i] = nxt[nxt[i]];
		}
	}
	int sum = 0, cnt = 0, rt = a[pre[l]];
	for(int i = l; i <= r; i = suf[i]) ++sum, cnt += a[i] > 0;
	sum = (sum + 1) / 2; if(cnt > sum) fail();
	for(int i = suf[l]; i <= r; i = suf[i]) 
		if(!a[i] && cnt < sum) a[i] = newnode(), ++cnt;
	if(sum == 1 && cnt == 0) a[l] = newnode();
	for(int i = l; suf[i] <= r; i = suf[i]){
		while(i > l && suf[i] <= r && !a[pre[i]] && a[i] && a[suf[i]])
			a[pre[i]] = a[suf[i]], del(i, suf[i]), i = pre[pre[i]];
		while(i >= l && suf[suf[i]] <= r && a[i] && a[suf[i]] && !a[suf[suf[i]]])
			a[suf[suf[i]]] = a[i], del(suf[i], suf[suf[i]]), i = pre[i];
	}
	for(int i = l; i <= r; i = suf[i]) if(!a[i]) a[i] = rt;
}
int main(){
	n = read(), m = n * 2 - 1;
	for(int i = 1; i <= m; i++) a[i] = read();
	if(a[1] && a[m] && a[1] != a[m]) fail();
	a[1] = a[m] = a[1] | a[m];
	for(int i = 0; i <= m; i++) pre[i] = i - 1, suf[i] = i + 1;
	for(int i = m; i; i--) if(a[i]) nxt[i] = vis[a[i]], vis[a[i]] = i;
	Solve(1, m);
	for(int i = 1; i <= m; i++) cout << a[i] << " ";
	return 0;
}

T3:
在这里插入图片描述
题意:给一个无向图,分配方向,使得最大出度最小
先二分答案,考虑到对于边 ( x , y ) (x,y) (x,y),要么 x 的出度 +1, 要么 y + 1
于是把每一条边作为一个点,原点向边连流量为1的边
每一条边向两个点连一条流量为1的边,每个点向汇点连流量为 m i d mid mid 的边
判断最大流是否等于 m 即可

#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], tot;
void add(int x, int y, int z){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
	nxt[++tot] = first[y], first[y] = tot, to[tot] = x, w[tot] = 0;
}
const int inf = 1e9;
int n, m, x[N], y[N];
int st, ed, dis[N];
bool bfs(){
	queue<int> q; q.push(st);
	memset(dis, -1, sizeof(dis)); dis[st] = 0;
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; if(dis[t] == -1 && w[i]){
				dis[t] = dis[x] + 1; q.push(t);
				if(t == ed) return true;
			}
		}
	} return false;
}
int dfs(int u, int flow){
	if(u == ed) return flow;
	int ans = 0; 
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(dis[t] == dis[u] + 1){
			int delta = dfs(t, min(w[i], flow));
			w[i] -= delta; w[i^1] += delta;
			flow -= delta; ans += delta;
			if(!flow) break;
		}
	} if(flow) dis[u] = -1; return ans;
}
int dinic(){ int ans = 0; while(bfs()) ans += dfs(st, inf); return ans;}
bool check(int mid){
	memset(first, 0, sizeof(first)); tot = 1;
	for(int i = 1; i <= m; i++) add(st, i, 1);
	for(int i = 1; i <= m; i++) add(i, x[i] + m, 1), add(i, y[i] + m, 1);
	for(int i = 1; i <= n; i++) add(i + m, ed, mid);
	return dinic() == m;
}
int main(){
	n = read(), m = read(); ed = n + m + 1;
	for(int i = 1; i <= m; i++) x[i] = read(), y[i] = read();
	int l = 1, r = m;
	while(l < r){
		int mid = (l+r) >> 1;
		if(check(mid)) r = mid; else l = mid + 1;
	} cout << l; 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、付费专栏及课程。

余额充值