T1:
先前缀和一波,题目转换为两个人,每次选一个数做为自己的分数,每次都只能在对方后面选
冷静分析一下(假装自己在玩游戏)
首先,如果我选了最后一个,游戏结束,相差
S
n
S_n
Sn
如果我选了倒数第二个,那么他只能选最后,相差
S
n
−
1
−
S
n
S_{n-1}-S_n
Sn−1−Sn
如果我选了倒数第三个,那么他以他的最优策略一定会选倒二和最后的最大的那个
于是倒过来处理一个后缀最大
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,Si−mxi+1)
T2:CF1053E Euler tour
首先看什么是不合法的:
1 和
2
∗
n
−
1
2*n-1
2∗n−1 不等
存在
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;
}