manacher + 前缀和,点分治 + 贪心 + 双指针
# A. 花海漫步
原题 。
首先要想到的一点是正难则反,答案转化成总回文串个数减去不相交的回文串个数。
我们记 分别表示以 为结尾的回文串个数和以 开头的回文串个数。
那么答案就是:
关于 如何求?
我们只需要在 过程中维护差分数组,最后做一遍前缀和就行了。
#include <bits/stdc++.h> | |
#define pb push_back | |
#define int long long | |
using namespace std; | |
namespace IO{ | |
inline int read(){ | |
int x = 0, f = 1; | |
char ch = getchar(); | |
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();} | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); | |
return x * f; | |
} | |
template <typename T> inline void write(T x){ | |
if(x < 0) putchar('-'), x = -x; | |
if(x > 9) write(x / 10); | |
putchar(x % 10 + '0'); | |
} | |
} | |
using namespace IO; | |
const int N = 4e6 + 10; | |
const int mod = 1e9 + 7; | |
int n, tot, ans; | |
char str[N], s[N]; | |
int p[N], f[N], g[N]; | |
inline int qpow(int a, int b){ | |
int res = 1; | |
while(b){ | |
if(b & 1) res = 1ll * res * a % mod; | |
a = 1ll * a * a % mod, b >>= 1; | |
} | |
return res; | |
} | |
inline void manacher(){ | |
for(int i = 1; i <= n; ++i) | |
s[(i << 1) - 1] = '*', s[i << 1] = str[i]; | |
s[0] = '#', s[(n << 1) + 1] = '*'; | |
n = (n << 1) + 1; | |
int id = 0, r = 0; | |
for(int i = 1; i <= n; ++i){ | |
if(i < r) p[i] = min(r - i, p[(id << 1) - i]); | |
else p[i] = 1; | |
while(s[i - p[i]] == s[i + p[i]]) p[i]++; | |
if(i + p[i] > r) id = i, r = i + p[i]; | |
tot = (tot + p[i] / 2) % mod; | |
f[i - p[i] + 1]++, f[i + 1]--, g[i]++, g[i + p[i]]--; | |
} | |
} | |
signed main(){ | |
#ifndef ONLINE_JUDGE | |
freopen("test.in", "r", stdin); | |
freopen("test.out", "w", stdout); | |
#endif | |
n = read(), scanf("%s", str + 1); | |
manacher(); | |
for(int i = 1; i <= n; ++i) | |
f[i] += f[i - 1], g[i] += g[i - 1]; | |
ans = 1ll * tot * (tot - 1) % mod * qpow(2, mod - 2) % mod; | |
// cout << "ans " << ans << " " << tot << endl; | |
int sum = 0; | |
for(int i = 2; i <= n - 2; i += 2) | |
sum = (sum + g[i]) % mod, ans = ((ans - 1ll * sum * f[i + 2] % mod) % mod + mod) % mod; | |
write(ans), puts(""); | |
return 0; | |
} |
# B. 收购城市
首先有一个非常重要的结论。
定义 表示 到其他所有城市的 路径个数。
判断一个子段 是否合法,只需要判断:
证明可以自己推推式子,大概就是两边没有用的都可以抵消掉。
剩下的问题就是如何求 了。
我们考虑使用点分治。对于一个分治点的所有儿子,开个桶记录已经走过的点的大小城市个数差,前缀和查询一遍再后缀和查询一遍即可。
#include <bits/stdc++.h> | |
#define pb push_back | |
#define int long long | |
using namespace std; | |
namespace IO{ | |
inline int read(){ | |
int x = 0, f = 1; | |
char ch = getchar(); | |
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();} | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); | |
return x * f; | |
} | |
template <typename T> inline void write(T x){ | |
if(x < 0) putchar('-'), x = -x; | |
if(x > 9) write(x / 10); | |
putchar(x % 10 + '0'); | |
} | |
} | |
using namespace IO; | |
const int N = 1e5 + 10; | |
int n; | |
int a[N], p[N], id[N]; | |
vector <int> G[N]; | |
int siz[N], mx[N], rt, vis[N]; | |
inline void get_root(int x, int fa, int all){ | |
siz[x] = 1, mx[x] = 0; | |
for(auto y : G[x]){ | |
if(y == fa || vis[y]) continue; | |
get_root(y, x, all), siz[x] += siz[y]; | |
mx[x] = max(mx[x], siz[y]); | |
} | |
mx[x] = max(mx[x], all - siz[x]); | |
if(!rt || mx[rt] > mx[x]) rt = x; | |
} | |
unordered_map <int, int> mp; | |
int s[N]; | |
inline void qry(int x, int fa, int now){ | |
s[id[x]] += mp[-now]; | |
for(auto y : G[x]) | |
if(y != fa && !vis[y]) qry(y, x, now + a[y]); | |
} | |
inline void upd(int x, int fa, int now){ | |
mp[now]++; | |
for(auto y : G[x]) | |
if(y != fa && !vis[y]) upd(y, x, now + a[y]); | |
} | |
inline void calc(int x){ | |
mp.clear(), mp[a[x]]++; | |
reverse(G[x].begin(), G[x].end()); | |
for(auto y : G[x]) | |
if(!vis[y]) qry(y, x, a[y]), upd(y, x, a[x] + a[y]); | |
mp.clear(); | |
reverse(G[x].begin(), G[x].end()); | |
for(auto y : G[x]) | |
if(!vis[y]) qry(y, x, a[y]), upd(y, x, a[x] + a[y]); | |
s[id[x]] += mp[0]; | |
} | |
inline void solve(int x){ | |
vis[x] = 1, calc(x); | |
for(auto y : G[x]){ | |
if(vis[y]) continue; | |
mx[rt = 0] = n, get_root(y, 0, siz[y]), solve(rt); | |
} | |
} | |
signed main(){ | |
#ifndef ONLINE_JUDGE | |
freopen("test.in", "r", stdin); | |
freopen("test.out", "w", stdout); | |
#endif | |
n = read(); | |
for(int i = 1; i <= n; ++i){ | |
a[i] = read(); | |
if(!a[i]) a[i] = -1; | |
} | |
for(int i = 1; i < n; ++i){ | |
int u = read(), v = read(); | |
G[u].pb(v), G[v].pb(u); | |
} | |
for(int i = 1; i <= n; ++i) id[p[i] = read()] = i; | |
get_root(1, 0, n), solve(rt); | |
for(int i = 1; i <= n; ++i) s[i] += s[i - 1]; | |
int ans = 0; | |
for(int i = 1, j = 0; i <= n; ++i){ | |
while(j < i && s[i] - s[j] > s[n] - s[i] + s[j]) j++; | |
ans += j; | |
} | |
write(ans), puts(""); | |
cerr << clock() << endl; | |
return 0; | |
} |
# C. 圣战救援
似乎是道披着计算几何皮的结论题。
不过还不会。