贪心 + 对顶堆,AC 自动机 + 树状数组,Tarjan 缩点 + 拓扑排序
# A. 「APIO2015」巴邻旁之桥 Palembang Bridges
刚开始的时候没啥思路,然后看到数据范围 或 ?!
首先把不需要过河的人都排除掉,然后考虑 的情况,那么每一组点对都要经过这个桥,所以就是所有点到这个桥的距离之和,显然中位数是最优的,于是把所有点都存下来求个中位数即可。
再来看 的情况,我们可以枚举一个断点,强制令左边的点都从左边的桥走,右边的点都从右边的桥走,所以整个对顶堆快速维护中位数即可。
关于对顶堆维护中位数 P1168 中位数
#include <bits/stdc++.h> | |
#define ll long long | |
using namespace std;  | |
namespace IO{  | |
inline ll read(){  | |
ll x = 0;  | |
char ch = getchar();  | |
while(!isdigit(ch)) ch = getchar();  | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();  | |
return x;  | |
    } | |
template <typename T> inline void write(T x){  | |
if(x > 9) write(x / 10);  | |
putchar(x % 10 + '0');  | |
    } | |
} | |
using namespace IO;  | |
const ll N = 3e5 + 10;  | |
const ll INF = 1e18;  | |
ll m, n;  | |
char op1[5], op2[5];  | |
struct node{  | |
ll x, y;  | |
inline bool operator < (const node &b) const{  | |
return x + y < b.x + b.y;  | |
    } | |
}per[N];  | |
ll p[N];  | |
ll tot, cnt, ans;  | |
priority_queue <ll> q1, q2;  | |
ll sum1, sum2, s[N];  | |
inline void Push(ll x){  | |
if(q1.empty() || x <= q1.top()) q1.push(x), sum1 += x;  | |
else q2.push(-x), sum2 += x;  | |
if(q2.size() + 1 < q1.size()){  | |
ll now = q1.top();  | |
q1.pop(), q2.push(-now);  | |
sum1 -= now, sum2 += now;  | |
    } | |
if(q1.size() + 1 < q2.size()){  | |
ll now = q2.top();  | |
q2.pop(), q1.push(-now);  | |
sum1 -= now, sum2 += now;  | |
    } | |
} | |
inline void solve1(){  | |
sort(p + 1, p + 1 + tot);  | |
for(ll i = 1; i <= tot; ++i) ans += abs(p[i] - p[cnt]);  | |
write(ans), puts("");  | |
} | |
inline void solve2(){  | |
sort(per + 1, per + 1 + cnt);  | |
for(ll i = 1; i <= cnt; ++i){  | |
Push(per[i].x), Push(per[i].y);  | |
s[i] = sum2 - sum1;  | |
    } | |
while(!q1.empty()) q1.pop();  | |
while(!q2.empty()) q2.pop();  | |
sum1 = sum2 = 0;  | |
for(ll i = cnt; i > 0; --i){  | |
s[i] += sum2 - sum1;  | |
Push(per[i].x), Push(per[i].y);  | |
    } | |
ll mins = INF;  | |
for(ll i = 1; i <= cnt; ++i) mins = min(mins, s[i]);  | |
write(ans + mins), puts("");  | |
} | |
signed main(){  | |
    // freopen("A.in", "r", stdin); | |
    // freopen("A.out", "w", stdout); | |
m = read(), n = read();  | |
for(ll i = 1, x, y; i <= n; ++i){  | |
scanf("%s%lld%s%lld", op1, &x, op2, &y);  | |
if(op1[0] == op2[0]) {ans += abs(x - y); continue;}  | |
per[++cnt] = (node){x, y};  | |
p[++tot] = x, p[++tot] = y, ans++;  | |
    } | |
if(!cnt) return write(ans), puts(""), 0;  | |
if(m == 1) solve1();  | |
else solve2();  | |
return 0;  | |
} | 
# B. 「NOI2011」阿狸的打字机
虽然之前做过一遍,但是不会了 QwQ
# 40pts 暴力
暴力建出 自动机,对于每组询问暴力在自动机上跑。
# 60pts 暴力
观察到有许多重复的询问,所以把询问离线下来,按 排序,开个桶一块询问。
建出 自动机后,把 指针倒过来,此时问题就变成了查询 在从根到 这条链中出现了多少次,所以找出 序,把根到 这条链全都标记为 1(这个操作可以用树状数组解决),然后 暴力往下跳。
# 100pts
感觉上面这种做法已经很优了,为什么还是过不了呢?
很简单,因为有很多串多次插入树状数组,所以同样把询问离线下来,按 排个序,统一处理即可。
#include <bits/stdc++.h> | |
using namespace std;  | |
namespace IO{  | |
inline int read(){  | |
int x = 0;  | |
char ch = getchar();  | |
while(!isdigit(ch)) ch = getchar();  | |
while(isdigit(ch)) x =(x << 3) +(x << 1) + ch - '0', ch = getchar();  | |
return x;  | |
    } | |
template <typename T> inline void write(T x){  | |
if(x > 9) write(x / 10);  | |
putchar(x % 10 + '0');  | |
    } | |
} | |
using namespace IO;  | |
const int N = 2e5 + 10;  | |
struct node{  | |
int v, nxt;  | |
} edge[N];  | |
int head[N], cnt;  | |
struct Query{  | |
int x, y, id, ans;  | |
inline bool operator < (const Query &b) const{  | |
return y < b.y;  | |
    } | |
} q[N << 1];  | |
char s[N];  | |
int n, m;  | |
int trie[N][27], tot;  | |
int fa[N], fail[N], End[N], num[N];  | |
int vis[N][27];  | |
int in[N], out[N], tim;  | |
int c[N], ans[N];  | |
int ql[N], qr[N];  | |
void add(int x, int y){  | |
edge[++cnt] =(node){y, head[x]};  | |
head[x] = cnt;  | |
} | |
void init(){  | |
scanf("%s", s + 1);  | |
int now = 0;  | |
int len = strlen(s + 1);  | |
for(int i = 1; i <= len; i++){  | |
if(s[i] >= 'a' && s[i] <= 'z'){  | |
if(!trie[now][s[i] - 'a']) trie[now][s[i] - 'a'] = ++tot, fa[tot] = now;  | |
now = trie[now][s[i] - 'a'];  | |
        } | |
if(s[i] == 'B') now = fa[now];  | |
if(s[i] == 'P') End[++n] = now, num[now] = n;  | |
    } | |
} | |
void build(){  | |
queue<int> q;  | |
for(int i = 0; i < 26; i++)  | |
if(trie[0][i]) q.push(trie[0][i]);  | |
while(!q.empty()){  | |
int now = q.front();  | |
q.pop();  | |
for(int i = 0; i < 26; i++){  | |
if(trie[now][i])  | |
fail[trie[now][i]] = trie[fail[now]][i], q.push(trie[now][i]);  | |
else trie[now][i] = trie[fail[now]][i];  | |
        } | |
    } | |
} | |
void dfs(int x){  | |
in[x] = ++tim;  | |
for(int i = head[x]; i; i = edge[i].nxt) dfs(edge[i].v);  | |
out[x] = tim;  | |
} | |
void update(int x, int y){  | |
for(; x <= tim; x +=(x &(-x))) c[x] += y;  | |
} | |
int query(int x){  | |
int res = 0;  | |
for(; x > 0; x -=(x &(-x))) res += c[x];  | |
return res;  | |
} | |
void solve(int x){  | |
update(in[x], 1);  | |
if(num[x])  | |
for(int i = ql[num[x]]; i <= qr[num[x]]; i++)  | |
q[i].ans = query(out[End[q[i].x]]) - query(in[End[q[i].x]] - 1);  | |
for(int i = 0; i < 26; i++)  | |
if(vis[x][i]) solve(vis[x][i]);  | |
update(in[x], -1);  | |
} | |
int main(){  | |
init();  | |
for(int i = 0; i <= tot; i++)  | |
for(int j = 0; j < 26; j++)  | |
vis[i][j] = trie[i][j];  | |
build();  | |
for(int i = 1; i <= tot; i++) add(fail[i], i);  | |
dfs(0);  | |
scanf("%d", &m);  | |
for(int i = 1; i <= m; i++) q[i] = (Query){read(), read(), i, 0};  | |
sort(q + 1, q + 1 + m);  | |
for(int i = 1, pos = 1; i <= m; i = pos){  | |
ql[q[i].y] = i;  | |
while(q[i].y == q[pos].y) pos++;  | |
qr[q[i].y] = pos - 1;  | |
    } | |
solve(0);  | |
for(int i = 1; i <= m; i++) ans[q[i].id] = q[i].ans;  | |
for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);  | |
return 0;  | |
} | 
# C. 「CEOI2011」Traffic
观察到这么一句话:保证边在交点以外的任何地方不相交。

(图源自:ppp204 巨佬的题解)
然后把中间到不了的点都删掉,剩下的点就是右边一段连续的区间。
因此我们只需要 一遍跑出它能到达的最高点与最低点,就计算出了答案。
由于会有环,所以 缩点一下。
#include <bits/stdc++.h> | |
using namespace std;  | |
namespace IO{  | |
inline int read(){  | |
int x = 0;  | |
char ch = getchar();  | |
while(!isdigit(ch)) ch = getchar();  | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();  | |
return x;  | |
    } | |
template <typename T> inline void write(T x){  | |
if(x > 9) write(x / 10);  | |
putchar(x % 10 + '0');  | |
    } | |
} | |
using namespace IO;  | |
const int N = 3e5 + 10;  | |
const int M = 9e5 + 10;  | |
const int INF = 2e9;  | |
int n, m, A, B;  | |
struct node{  | |
int x, y, id;  | |
inline bool operator < (const node &b) const{  | |
return y > b.y;  | |
    } | |
}p[N];  | |
vector <int> g[N], G[N];  | |
vector <node> L, R;  | |
int ans[N];  | |
int mx[N], mn[N];  | |
int dfn[N], low[N], tim;  | |
int stk[N], vis[N], top;  | |
int scc[N], cnt;  | |
inline void tarjan(int x){  | |
dfn[x] = low[x] = ++tim;  | |
stk[++top] = x, vis[x] = 1;  | |
if(p[x].x == A) R.push_back(p[x]);  | |
for(auto y : g[x]){  | |
if(!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);  | |
else if(vis[y]) low[x] = min(low[x], dfn[y]);  | |
    } | |
if(dfn[x] == low[x]){  | |
int k; cnt++;  | |
do{  | |
k = stk[top--];  | |
vis[k] = 0;  | |
scc[k] = cnt;  | |
}while(x != k);  | |
    } | |
} | |
inline void dfs(int x){  | |
if(vis[x]) return;  | |
vis[x] = 1;  | |
for(auto y : G[x]){  | |
dfs(y);  | |
mx[x] = max(mx[x], mx[y]), mn[x] = min(mn[x], mn[y]);  | |
    } | |
} | |
int main(){  | |
n = read(), m = read(), A = read(), B = read();  | |
for(int i = 1; i <= n; ++i) p[i] = (node){read(), read(), i};  | |
for(int i = 1; i <= m; ++i){  | |
int x = read(), y = read(), k = read();  | |
g[x].push_back(y);  | |
if(k == 2) g[y].push_back(x);  | |
    } | |
for(int i = 1; i <= n; ++i){  | |
mx[i] = 0, mn[i] = INF;  | |
if(!p[i].x){  | |
L.push_back(p[i]);  | |
if(!dfn[i]) tarjan(i);  | |
        } | |
    } | |
for(int x = 1; x <= n; ++x)  | |
for(auto y : g[x])  | |
if(scc[x] != scc[y]) G[scc[x]].push_back(scc[y]);  | |
sort(L.begin(), L.end());  | |
sort(R.begin(), R.end());  | |
for(auto x : R){  | |
int now = scc[x.id], res = lower_bound(R.begin(), R.end(), x) - R.begin() + 1;  | |
mx[now] = max(mx[now], res);  | |
mn[now] = min(mn[now], res);  | |
    } | |
memset(vis, 0, sizeof(vis));  | |
for(auto u : L){  | |
dfs(scc[u.id]);  | |
write(max(mx[scc[u.id]] - mn[scc[u.id]] + 1, 0)), puts("");  | |
    } | |
return 0;  | |
} |