​CF793G Oleg and chess​​​

首先暴力就是个二分图匹配,行向列连边
想到了线段树优化建图
于是有一种做法是先扫描线,然后每一列的线段树暴力连边
考虑到每一次只会有一段区间被修改,于是类似主席树一样
对于修改的节点我们行建一个点向下连边
然后就是 省选模拟 19/10/07_git 的模板

#include<bits/stdc++.h>
#define N 40050
#define M 4000050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = 0;}
while(isdigit(ch)) cnt = (cnt + (cnt << 2) << 1) + (ch ^ 48), ch = getchar();
return f ? cnt : -cnt;
}
const int inf = 1e9;
struct opt{ int x, l, r, f;};
vector<opt> v[N];
int n, m, st, ed, node;
int first[M], f2[M], nxt[M], to[M], w[M], tot = 1;
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;
}
struct SGT{
int pos[M], cov[N];
void pushup(int x){
pos[x] = ++node;
if(!cov[x<<1]) add(pos[x], pos[x<<1], inf);
if(!cov[x<<1|1]) add(pos[x], pos[x<<1|1], inf);
}
void build(int x, int l, int r){
if(l == r){ pos[x] = n + l; return; }
int mid = (l+r) >> 1; build(x<<1, l, mid); build(x<<1|1, mid+1, r);
pushup(x);
}
void modify(int x, int l, int r, int L, int R, int k){
if(L<=l && r<=R){ cov[x] += k; return; }
int mid = (l+r) >> 1;
if(L<=mid) modify(x<<1, l, mid, L, R, k);
if(R>mid) modify(x<<1|1, mid+1, r, L, R, k);
pushup(x);
}
}Seg;
int dis[M];
bool bfs(){
queue<int> q; q.push(st);
memset(dis, -1, sizeof(int)*(node+1)); 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(w[i] && dis[t] == -1){
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 = f2[u]; i; i = nxt[i]){
int t = to[i]; if(dis[t] == dis[u] + 1){
int delta = dfs(t, min(flow, w[i]));
flow -= delta; ans += delta;
w[i] -= delta; w[i ^ 1] += delta;
if(!flow) break;
}
} if(flow) dis[u] = -1; return ans;
}
int dinic(){
int ans = 0; while(bfs()){
for(int i = 0; i <= node; i++) f2[i] = first[i];
ans += dfs(st, inf);
}
return ans;
}
int main(){
n = read(), m = read();
for(int i = 1; i <= m; i++){
int x1 = read(), y1 = read(), x2 = read(), y2 = read();
v[x1].push_back((opt){x1, y1, y2, 1});
v[x2+1].push_back((opt){x2 + 1, y1, y2, -1});
} st = 0; ed = n * 2 + 1; node = ed + 1;
for(int i = 1; i <= n; i++) add(st, i, 1), add(i + n, ed, 1);
Seg.build(1, 1, n);
for(int i = 1; i <= n; i++){
for(int j = 0; j < v[i].size(); j++){
opt x = v[i][j]; Seg.modify(1, 1, n, x.l, x.r, x.f);
}
if(!Seg.cov[1]) add(i, Seg.pos[1], inf);
} cout << dinic(); return 0;
}

​CF335E Counting Skyscrapers​

省选模拟 19/10/07_git_02省选模拟 19/10/07_#define_03
看样例觉得有点可疑,好像输出 省选模拟 19/10/07_#define_04 就可以了,下面来证明一下
只需要证明 省选模拟 19/10/07_git_02省选模拟 19/10/07_#define_06 期望经过的点为 省选模拟 19/10/07_git_07 个即可
我们假设已经出现了一个 省选模拟 19/10/07_i++_08 层的楼,考虑下一个超过 省选模拟 19/10/07_i++_08 层的楼的出现位置
为了方便,我们记 省选模拟 19/10/07_#define_10省选模拟 19/10/07_git_11 的出现概率,省选模拟 19/10/07_i++_12
结尾出现 省选模拟 19/10/07_i++_13 的概率为 省选模拟 19/10/07_#define_14,中间的必须 省选模拟 19/10/07_git_15,概率为 省选模拟 19/10/07_#define_16
那么中间的期望就是 省选模拟 19/10/07_i++_17


省选模拟 19/10/07_#define_03省选模拟 19/10/07_git_02
考虑增量,每次将 省选模拟 19/10/07_i++_20 的上限往上提
省选模拟 19/10/07_i++_21 时,答案显然为 省选模拟 19/10/07_#define_04
h 增加 1 时,考虑期望增加多少,期望减少多少
枚举高度为 省选模拟 19/10/07_i++_23 的覆盖长度 省选模拟 19/10/07_i++_24
显然有 省选模拟 19/10/07_i++_25 个点供它作为起始覆盖位置
然后两头的点都 省选模拟 19/10/07_git_26 的概率是 省选模拟 19/10/07_#define_27
在两头都省选模拟 19/10/07_git_26 的概率的概率下,考虑覆盖的高度为 省选模拟 19/10/07_i++_20 的期望
首先 省选模拟 19/10/07_i++_20 出现的概率是 省选模拟 19/10/07_git_31
长度为 省选模拟 19/10/07_i++_24 的区间内有 省选模拟 19/10/07_i++_33 个需要放,期望个数就是 省选模拟 19/10/07_i++_34
然后区间需要全部小于等于 h,概率为 省选模拟 19/10/07_i++_35
所以
省选模拟 19/10/07_i++_36

#include<bits/stdc++.h>
#define N 30050
#define H 35
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;
}
typedef long double ld;
char op[10];
int n, h;
ld pw[H << 1];
ld power(ld a, int b){ld ans=1; for(;b;b>>=1){if(b&1)ans=ans*a; a=a*a;} return ans;}
void Alice(){
pw[0] = 1.0;
for(int i = 1; i <= (h << 1); i++) pw[i] = pw[i-1] * 2.0;
ld ans = n;
for(int i = 1; i <= h; i++){
for(int j = 1; j <= n; j++){
ans += (n - j) * (1 / pw[i << 1]) * power(1 - 1 / pw[i], j - 1) * (pw[i] - pw[i-1] * (1 + (j - 1) / (pw[i] - 1)));
}
} printf("%.9lf", (double)ans);
}
void Bob(){ printf("%.9lf", (double)n); }
int main(){
scanf("%s", op);
n = read(), h = read();
if(op[0] == 'A'){ Alice(); return 0; }
if(op[0] == 'B'){ Bob(); return 0; }
}

​CF809E Surprise me!​​​

显然 省选模拟 19/10/07_git_37
那么是哪里错了呢
省选模拟 19/10/07_i++_38
省选模拟 19/10/07_git_39
省选模拟 19/10/07_i++_40
显然对于省选模拟 19/10/07_#define_41 公共的 省选模拟 19/10/07_#define_42,乘了两次但应当只乘一次
于是要乘上 省选模拟 19/10/07_git_43
于是乎有 省选模拟 19/10/07_#define_44
于是将式子化简就是
省选模拟 19/10/07_#define_45
枚举 gcd
省选模拟 19/10/07_#define_46
省选模拟 19/10/07_#define_47省选模拟 19/10/07_#define_48 的情况,先求出 省选模拟 19/10/07_#define_49,然后可以莫比乌斯反演回去
省选模拟 19/10/07_git_50 的踢出来求 省选模拟 19/10/07_git_51
省选模拟 19/10/07_i++_52
省选模拟 19/10/07_#define_53
于是树形 省选模拟 19/10/07_i++_54省选模拟 19/10/07_i++_55 处考虑一下贡献
树形 dp 可以将特殊点拿出来做虚树,复杂度 省选模拟 19/10/07_git_56

#include<bits/stdc++.h>
#define N 400050
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;
}
typedef long long ll;
const int Mod = 1e9 + 7;
int inc(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
void Add(int &a, int b){ a = inc(a, b);}
int mul(int a, int b){ return 1ll * a * b % Mod;}
int power(int a, int b){ int ans = 1; for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);} return ans;}
int first[N], nxt[N << 1], to[N << 1], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int n, a[N], p[N], dfn[N], ed[N], sign, fa[N][20], dep[N];
int phi[N]; bool isp[N]; int prim[N], cnt;
int F[N];
void prework(){
phi[1] = 1;
for(int i = 2; i <= n; i++){
if(!isp[i]) prim[++cnt] = i, phi[i] = i-1;
for(int j = 1; j <= cnt; j++){
if(prim[j] * i > n) break;
isp[prim[j] * i] = 1;
if(i % prim[j] == 0){ phi[i * prim[j]] = phi[i] * prim[j]; break;}
phi[i * prim[j]] = phi[i] * phi[prim[j]];
}
}
}
void dfs(int u, int f){
dfn[u] = ++sign;
for(int i = 1; i <= 18; i++) fa[u][i] = fa[fa[u][i-1]][i-1];
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == f) continue;
fa[t][0] = u; dep[t] = dep[u] + 1; dfs(t, u);
} ed[u] = sign;
}
int lca(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
for(int i = 18; i >= 0; i--) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if(x == y) return x;
for(int i = 18; i >= 0; i--) if(fa[x][i] ^ fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
bool ck(int x, int y){ return dfn[x] <= dfn[y] && dfn[y] <= ed[x];}
vector<int> seq; bool in[N], vis[N];
bool cmp(int a, int b){ return dfn[a] < dfn[b];}
int sta[N], top;
vector<int> v[N];
int f[N], ans;
void dp(int u){
if(vis[u]) f[u] = phi[a[u]], Add(ans, mul(2, Mod - mul(dep[u], mul(f[u], f[u]))));
for(int i = 0; i < v[u].size(); i++){
int t = v[u][i]; dp(t);
Add(ans, mul(4, Mod - mul(dep[u], mul(f[u], f[t]))));
Add(f[u], f[t]);
} v[u].clear();
}
void calc(int d){
int sum1 = 0, sum2 = 0; ans = 0;
for(int t = d; t <= n; t += d){
seq.push_back(p[t]), in[p[t]] = vis[p[t]] = 1;
Add(sum1, phi[t]), Add(sum2, mul(phi[t], dep[p[t]]));
} Add(ans, mul(2, mul(sum1, sum2)));
sort(seq.begin(), seq.end(), cmp); int siz = seq.size();
for(int i = 0; i < siz - 1; i++){
int l = lca(seq[i], seq[i + 1]);
if(!in[l]) seq.push_back(l), in[l] = 1;
} sort(seq.begin(), seq.end(), cmp);
sta[top = 1] = seq[0];
for(int i = 1; i < seq.size(); i++){
while(top > 1 && !ck(sta[top], seq[i])) --top;
v[sta[top]].push_back(seq[i]); sta[++top] = seq[i];
}
while(top > 1) add(sta[top-1], sta[top]), top--;
dp(seq[0]); F[d] = ans;
for(int t = d + d; t <= n; t += d) Add(F[d], Mod - F[t]);
for(int i = 0; i < seq.size(); i++) f[seq[i]] = in[seq[i]] = vis[seq[i]] = 0; seq.clear();
}
int main(){
n = read(); prework();
for(int i = 1; i <= n; i++) a[i] = read(), p[a[i]] = i;
for(int i = 1; i < n; i++){
int x = read(), y = read();
add(x, y); add(y, x);
} dep[1] = 1; dfs(1, 0);
int sum = 0;
for(int i = n / 2; i >= 1; i--){
calc(i); Add(sum, mul(mul(power(phi[i], Mod - 2), i), F[i]));
} cout << mul(power(mul(n, n-1), Mod - 2), sum); return 0;
}