最近的题题解咕了好多,看着补吧。。。
A. 掌分治
直接按照连通块考虑没啥前途,根据期望的线型性,把贡献看成点对的贡献
设 \(f_{i, j}\) 表示当 \(i\) 为 根时, \(j\) 在其所在连通块的概率
求总和即为答案
考虑实际上限制的是 \(i\) 是 \(i - j\) 路径上第一个删掉的点,那么概率就是路径上点数分之一
考虑环如何容斥,其实就是顺时针联通+逆时针联通-全联通
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int mod = 998244353;
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
const int maxn = 405;
int n, m, inv[maxn];
vector<int>g[maxn + maxn], e[maxn];
int dfn[maxn], low[maxn], tim, st[maxn], top, cnt;
int f[maxn][maxn];
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
void dfs(int x){
dfn[x] = low[x] = ++tim;
st[++top] = x;
for(int v : e[x]){
if(dfn[v])low[x] = min(low[x], dfn[v]);
else{
dfs(v); low[x] = min(low[x], low[v]);
if(low[v] == dfn[x]){
++cnt; int y;
do{
y = st[top--];
g[cnt].push_back(y);
g[y].push_back(cnt);
}while(y != v);
g[cnt].push_back(x);
g[x].push_back(cnt);
}
}
}
}
void solve(int x, int fa){
if(x <= n){
for(int v : g[x])if(v != fa)solve(v, x);
return;
}
int pf = 0; while(g[x][pf] != fa)++pf;
for(int i = (pf + 1) % g[x].size(), a = 1; i != pf; i = (i + 1) % g[x].size(), ++a){
int b = g[x].size() - a, v = g[x][i];
for(int j = a + 1; j <= n; ++j)add(f[v][j], f[fa][j - a]);
for(int j = b + 1; j <= n; ++j)add(f[v][j], f[fa][j - b]);
for(int j = a + b; j <= n; ++j)add(f[v][j], mod - f[fa][j - a - b + 1]);
solve(v, x);
}
}
int main(){
freopen("cactus.in","r",stdin);
freopen("cactus.out","w",stdout);
n = read(), m = read();
for(int i = 1; i <= m; ++i){
int u = read(), v = read();
e[u].push_back(v); e[v].push_back(u);
}
inv[1] = 1; for(int i = 2; i <= n; ++i)inv[i] = (mod - 1ll * mod / i * inv[mod % i] % mod) % mod;
cnt = n; dfs(1); int ans = 0;
for(int i = 1; i <= n; ++i){
memset(f, 0, sizeof(f));
f[i][1] = 1; solve(i, 0);
for(int x = 1; x <= n; ++x)
for(int j = 1; j <= n; ++j)
ans = (ans + 1ll * f[x][j] * inv[j]) % mod;
}
printf("%d\n",ans);
return 0;
}
B. 图圈圈
你说得对,但是随机化是一款ZYM大佬自主研发的.....
通过证明得到 \(m >= n + 2 \sqrt n\) 时一定为 \(yes\)
然后考虑对剩下的边建立一棵生成树,对非树边涉及到的点建立虚树
然后以所有点为根都暴力 \(DFS\) 每次判断目标点能走回根节点才走
判断可以之际暴力做,这样最终复杂度是 \(n^2\) 的
但是我显然写的不是这个,针对数据比较水,大部分可以暴力 \(DFS\) 搜索次数大于阈值直接无解
否则随机建树,每次考虑加上非树边构成的环,用随机\(hash\) 区分不同的环,可以通过本题。
数据但凡强点就没了。但是ZYM大佬巨
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 20005;
int n, m;
struct Edge{int u, v; ll val;}re[1000005], fe[1000005];
mt19937_64 rd((ull)&maxn);
struct DSU{
int f[maxn];
void init(){for(int i = 1; i <= n; ++i)f[i] = i;}
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
bool merge(int x, int y){
x = fa(x); y = fa(y);
if(x == y)return false;
f[y] = x;
return true;
}
}S;
int head[maxn], tot;
struct edge{int to, net; ll val;}e[maxn << 1];
void add(int u, int v, ll w){
e[++tot] = {v, head[u], w};
head[u] = tot;
}
int dep[maxn], fa[maxn], son[maxn], top[maxn], si[maxn];
ll sum[maxn], rlen[maxn];
void dfs1(int x){
si[x] = 1; son[x] = 0;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa[x])continue;
fa[v] = x; dep[v] = dep[x] + 1; sum[v] = sum[x] ^ e[i].val;
dfs1(v); si[x] += si[v];
if(si[son[x]] < si[v])son[x] = v;
}
}
void dfs2(int x, int tp){
top[x] = tp;
if(son[x])dfs2(son[x], tp);
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v != fa[x] && v != son[x])dfs2(v, v);
}
}
void clear(){
for(int i = 1; i <= n; ++i)head[i] = 0;
for(int i = 1; i <= n; ++i)dep[i] = 0;
tot = 0;
}
int LCA(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] > dep[top[v]])u = fa[top[u]];
else v = fa[top[v]];
}
return dep[u] > dep[v] ? v : u;
}
void solve(){
shuffle(re + 1, re + m + 1, rd);
S.init(); int cnt = 0;
for(int i = 1; i <= m; ++i)if(S.merge(re[i].u, re[i].v)){
add(re[i].u, re[i].v, re[i].val);
add(re[i].v, re[i].u, re[i].val);
}else fe[++cnt] = re[i];
for(int i = 1; i <= n; ++i)if(!dep[i]){
dep[i] = 1; fa[i] = 0; sum[i] = 0; dfs1(i); dfs2(i, i);
}
for(int i = 1; i <= cnt; ++i){
int lca = LCA(fe[i].u, fe[i].v);
int len = dep[fe[i].u] + dep[fe[i].v] - 2 * dep[lca];
ll val = sum[fe[i].u] ^ sum[fe[i].v] ^ fe[i].val;
if(rlen[len] && rlen[len] != val){
printf("Yes\n"); exit(0);
}
rlen[len] = val;
}
clear();
}
vector<int>g[maxn];
int s, dtim, len, cl[maxn]; bool vis[maxn];
void dfs(int x, int fa){
++dtim;
if(dtim > 2e8){
printf("No\n"); exit(0);
}
vis[x] = true;
++len;
for(int v : g[x]){
++dtim;
if(v != fa){
if(v == s){
++cl[len];
if(cl[len] == 3 && len >= 2){
printf("Yes\n"); exit(0);
}
}
if(!vis[v])dfs(v, x);
}
}
--len;
vis[x] = false;
}
void bf(){
for(int i = 1; i <= m; ++i){
int u = read(), v = read();
g[u].push_back(v); g[v].push_back(u);
}
for(int i = 1; i <= n; ++i)vis[i] = true;
for(s = 1; s <= n; ++s)dfs(s, s);
printf("No\n"); exit(0);
}
int main(){
// freopen("graph.in","r",stdin);
// freopen("graph.out","w",stdout);
n = read(); m = read();
if(m > n + 2 * sqrt(n)){printf("Yes\n"); return 0;}
if(n < 20000)bf();
for(int i = 1; i <= m; ++i)re[i].u = read(), re[i].v = read(), re[i].val = rd();
while(clock() < 1.9 * CLOCKS_PER_SEC)solve();
printf("No\n");
return 0;
}
C. 下大雨
\(woc\) 原 ! \(Delov\) 喜提大保底!
雨棚 \(a\) 在 \(b\) 上则由 \(a\) 向 \(b\) 连边,这样肯定得到一个 \(DAG\)
设 \(f_{i}\) 表示考虑若干个雨棚之后,在 \(i\) 处落雨的最小打孔数量
每个雨棚相当于对 \(f\) 按照从高到低取前/后缀 \(min\), 然后区间 \(+1\)
暴力干喜提 \(30 - 50 pts\)
发现复杂度在建图和 \(DP\) 上
对于建图可以扫描线,插入时向前驱后继连边
对于 \(DP\) 考虑维护差分,用两个 \(map\) 分别维护正/负差分值
对于区间 \(+1\) 可以直接干
区间取 \(min\) 以前缀为例,就是对于正数差分值扔掉,在后面按顺序找负数差分值消掉,区间内消完的话,将没消掉的部分放在区间后面
可以想想实际过程
后缀的话就是扔掉负差分
复杂度分析使用势能分析
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 5e5 + 55;
const double eps = 1e-8;
int L, R, n;
struct node{
int x1, y1, x2, y2;
double calc(int x){return y1 + (x - x1) * (double)(y2 - y1) / (double)(x2 - x1);}
bool in(int x){return min(x1, x2) <= x && max(x1, x2) >= x;}
}d[maxn];
int tmp[maxn * 8], cnt;
int deg[maxn * 8]; vector<int>g[maxn * 8], rl[maxn * 8], rr[maxn * 8];
struct data{
int id;
friend bool operator < (const data &x, const data &y){
if(x.id == y.id)return false;
if(d[y.id].in(d[x.id].x1))return d[x.id].y1 - eps > d[y.id].calc(d[x.id].x1);
else if(d[y.id].in(d[x.id].x2))return d[x.id].y2 - eps > d[y.id].calc(d[x.id].x2);
else return d[x.id].calc(d[y.id].x1) - eps > d[y.id].y1;
}
};
set<data>s;
map<int, int>mp1, mp2;
int f[maxn * 8];
queue<int>q;
int main(){
freopen("rain.in","r",stdin);
freopen("rain.out","w",stdout);
L = read() * 2, R = read() * 2; n = read();
for(int i = 1; i <= n; ++i){
d[i].x1 = read() * 2, d[i].y1 = read(), d[i].x2 = read() * 2, d[i].y2 = read();
tmp[++cnt] = d[i].x1; tmp[++cnt] = d[i].x2;
tmp[++cnt] = d[i].x1 + 1; tmp[++cnt] = d[i].x1 - 1; tmp[++cnt] = d[i].x2 + 1; tmp[++cnt] = d[i].x2 - 1;
}
tmp[++cnt] = L; tmp[++cnt] = R; tmp[++cnt] = (L + R) / 2;
sort(tmp + 1, tmp + cnt + 1); cnt = unique(tmp + 1, tmp + cnt + 1) - tmp - 1;
for(int i = 1; i <= n; ++i){
int a = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x1) - tmp, b = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x2) - tmp;
rl[min(a, b)].push_back(i);
rr[max(a, b)].push_back(i);
}
for(int i = 1; i <= cnt; ++i){
for(int v : rl[i]){
s.insert(data{v});
auto it = s.lower_bound(data{v});
if(next(it) != s.end())g[v].push_back((*next(it)).id), ++deg[(*next(it)).id];
if(it != s.begin())g[(*prev(it)).id].push_back(v), ++deg[v];
}
for(int v : rr[i])s.erase(data{v});
}
L = lower_bound(tmp + 1, tmp + cnt + 1, L) - tmp; R = lower_bound(tmp + 1, tmp + cnt + 1, R) - tmp;
for(int i = 1; i <= n; ++i){
d[i].x1 = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x1) - tmp;
d[i].x2 = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x2) - tmp;
}
for(int i = 1; i <= n; ++i)if(deg[i] == 0)q.push(i);
mp1[0] = 0x3f3f3f3f; mp2[L] = 0x3f3f3f3f; mp1[R + 1] = 0x3f3f3f3f;
while(!q.empty()){
int now = q.front(); q.pop();
for(int v : g[now]){--deg[v]; if(deg[v] == 0)q.push(v);}
if(d[now].x1 < d[now].x2){
int p = d[now].x2 - 1, sum = 0, q = p;
while(true){
auto it = mp2.upper_bound(p);
if(it == mp2.begin())break;
it = prev(it); if((*it).first <= d[now].x1)break;
int ns = (*it).second; p = (*it).first; mp2.erase(it);
while(ns > 0 && q > d[now].x1){
it = mp1.upper_bound(min(p, q));
if(it == mp1.begin())break;
--it; if((*it).first <= d[now].x1)break;
q = (*it).first;
if((*it).second <= ns){
ns -= (*it).second;
mp1.erase(it);
}else{
mp1[q] -= ns;
ns = 0;
}
}
sum += ns;
}
if(sum){
if(mp1.count(d[now].x1)){
if(sum >= mp1[d[now].x1]){
sum -= mp1[d[now].x1];
mp1.erase(d[now].x1);
}
}
if(sum)mp2[d[now].x1] += sum;
}
if(mp2.count(d[now].x1 + 1)){--mp2[d[now].x1 + 1]; if(mp2[d[now].x1 + 1] == 0)mp2.erase(d[now].x1 + 1);}
else ++mp1[d[now].x1 + 1];
if(mp1.count(d[now].x2 + 1)){--mp1[d[now].x2 + 1]; if(mp1[d[now].x2 + 1] == 0)mp1.erase(d[now].x2 + 1);}
else ++mp2[d[now].x2 + 1];
}else{
int p = d[now].x2 + 1, sum = 0, q = p;
while(true){
auto it = mp1.lower_bound(p);
if(it == mp1.end() || (*it).first > d[now].x1)break;
int ns = (*it).second; p = (*it).first; mp1.erase(it);
while(ns > 0 && q <= d[now].x1){
it = mp2.lower_bound(max(q, p));
if(it == mp2.end() || (*it).first > d[now].x1)break;
q = (*it).first;
if((*it).second <= ns){
ns -= (*it).second;
mp2.erase(it);
}else{
mp2[q] -= ns;
ns = 0;
}
}
sum += ns;
}
if(sum){
if(mp2.count(d[now].x1 + 1)){
if(sum >= mp2[d[now].x1 + 1]){
sum -= mp2[d[now].x1 + 1];
mp2.erase(d[now].x1 + 1);
}
}
if(sum)mp1[d[now].x1 + 1] += sum;
}
if(mp2.count(d[now].x2)){--mp2[d[now].x2]; if(mp2[d[now].x2] == 0)mp2.erase(d[now].x2);}
else ++mp1[d[now].x2];
if(mp1.count(d[now].x1)){--mp1[d[now].x1]; if(mp1[d[now].x1] == 0)mp1.erase(d[now].x1);}
else ++mp2[d[now].x1];
}
}
int ans = 0x3f3f3f3f;
for(auto it : mp1)f[it.first] += it.second;
for(auto it : mp2)f[it.first] -= it.second;
for(int i = 1; i <= cnt; ++i)f[i] += f[i - 1];
for(int i = L; i <= R; ++i)ans = min(ans, f[i]);
printf("%d\n",ans);
return 0;
}