D
二分套二分
给一个集合,每次询问问不在这个集合里的,第k小的元素值?
可以二分这个值x,然后 c k e c k ckeck ckeck里二分得到集合里比这个值小的元素个数cnt,x-cnt就是这个值在不在集合里的数里的排名。
其实也可以离散化之后加到线段树里,然后线段树二分。
void solve() {
int n,q;
cin>>n>>q;
vi a;
rep(i,1,n){
int x;
cin>>x;
a.push_back(x);
}
sort(a.begin(),a.end());
auto check=[&](int x,int k)->bool{
int cnt=upper_bound(a.begin(),a.end(),x)-a.begin();
cnt=x-cnt;
return cnt>=k;
};
rep(i,1,q){
int x;
cin>>x;
int l=1,r=2e18;
while(l<=r){
int m=l+r>>1;
if(check(m,x))r=m-1;
else l=m+1;
}
cout<<l<<'\n';
}
}
E
卡特兰数变形
n个白球m个黑球,排成一列,给一个约束,形如:对于所有前缀,白球比黑球多的不超过j个,问满足所有约束的前提下的排列方案数
两种东西组成排列,如果是要求前i个里某种不超过j个,这就是卡特兰数的标准模型,这等价于问一个(0,0)出发到(n,m)的非减路径。不越过y=x的方案数。这里要求白球比黑球多不超过j个,则是稍作修改,变成求不越过 y = x + j y=x+j y=x+j的方案数。
不越过这条线的方案数,等于所有方案数减去越过这条线的方案数。所有方案显然就是 C ( n + m , m ) C(n+m,m) C(n+m,m),在n+m步里选n步往上走,m步往右走。
所有越过这条线的路径,都可以把
(
0
,
0
)
(0,0)
(0,0)到最后一次碰线之间这段路径,对于线做一个轴对称,得到一个往右走
m
+
j
+
1
m+j+1
m+j+1步,一共走
n
+
m
n+m
n+m步的路径。这两种路径是一一对应的,因此碰线的路径就等于
C
(
n
+
m
,
m
+
j
+
1
)
C(n+m,m+j+1)
C(n+m,m+j+1)
void solve() {
int n,m,k;
cin>>n>>m>>k;
if(n>m+k){
cout<<0;
}
else{
int ans=C(n+m,m)-C(n+m,m+k+1);
ans=(ans+M1)%M1;
cout<<ans;
}
}
F
网络流 建图优化
一个网格图,有n个棋子,每个棋子可以放在 [ l x , r x ] ∗ [ l y , r y ] [l_x,r_x]*[l_y,r_y] [lx,rx]∗[ly,ry]的区域内任意一点上,不能有两个棋子在同一行或同一列,问最多能放多少个棋子?
棋盘上放棋子,不能在一行或一列,这是经典网络流模型,建一个二分图,行左部,列右部,在 ( i , j ) (i,j) (i,j)放一个棋子,等价于第i行和第j列匹配,也就是左部点i和右部点j之间有一条边可以流。每一行每一列最多一个棋子,那么源点到每一行流量都是1,每一列到汇点流量都是1。问最多能放多少棋子,就是问最大流
这题的特殊点在于,每个棋子都可以放在一个矩形区域里,相当于一个区间里的行,和一个区间里的列之间都有连边。如果真的一一连边,肯定超时。但我们只关心连通性的话,其实可以用一个中继点,所有行,所有列都连到这个中继点即可。并且由于两个区间里我们只能选一组点连通,也就是只能用一次,可以把这个中继点拆成两个点,两点间流量为1。
最后,注意网络流建图,必须连上流量为0的反向边
int h[N * 2], e[N * 6], ne[N * 6], f[N * 6], idx, S, T, cur[N * 2], d[N * 2];
bool vis[N * 2];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], f[idx] = c, h[a] = idx++;
// e[idx] = a, ne[idx] = h[b], f[idx] = 0, h[b] = idx++;
}
bool bfs() {
memset(d, -1, sizeof(d));
memset(vis, 0, sizeof(vis));
queue<int> q;
q.push(S);
vis[S] = 1, d[S] = 0, cur[S] = h[S];
while (q.size()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (vis[j] || !f[i]) continue;
d[j] = d[t] + 1, vis[j] = 1, cur[j] = h[j];
if (j == T) return 1;
q.push(j);
}
}
return 0;
}
int dfs(int x, int limit) {
if (x == T) return limit;
int flow = 0;
for (int i = cur[x]; (~i) && flow < limit; i = ne[i]) {
int j = e[i];
cur[x] = i;
if (d[j] != d[x] + 1 || !f[i]) continue;
int k = dfs(j, min(f[i], limit - flow));
if (!k) d[j] = -1;
f[i] -= k;
f[i ^ 1] += k;
flow += k;
}
return flow;
}
int dinic() {
int ans = 0, flow;
while (bfs()) while (flow = dfs(S, 1e9)) ans += flow;
return ans;
}
void solve() {
int n,m,k;
cin>>n>>m>>k;
S=n+m+2*k+1;
T=S+1;
memset(h,-1,sizeof h);
rep(i,1,k){
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
int x=n+m+2*i-1;
int y=x+1;
rep(i,x1,x2){
add(i,x,1);
add(x,i,0);
}
rep(i,y1,y2){
add(y,i+n,1);
add(i+n,y,0);
}
add(x,y,1);
add(y,x,0);
}
rep(i,1,n){
add(S,i,1);
add(i,S,0);
}
rep(i,1,m){
add(i+n,T,1);
add(T,i+n,0);
}
cout<<dinic();
}