部分互测题,专项测试题题解
互测部分
1
https://www.cnblogs.com/Chencgy/p/16970117.html
2
A. 营救皮卡丘
跑弗洛伊德,搞出 不经过比 编号大的点的最小花费
每个点都要走一遍,套路的拆点补流
的流量直接从源点到
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 = 555;
const int inf = 0x3f3f3f3f;
int n, m, K, f[maxn][maxn];
struct MCMF{
struct edge{int to, net, val, cost;}e[maxn * maxn];
int head[maxn], dis[maxn], tot = 1, s, t;
bool vis[maxn];
queue<int>q;
void add(int u, int v, int w, int c){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
e[tot].val = w;
e[tot].cost = c;
}
void link(int u, int v, int w, int c){add(u, v, w, c); add(v, u, 0, -c);}
bool spfa(){
for(int i = 1; i <= t; ++i)dis[i] = inf, vis[i] = false;
q.push(s); dis[s] = 0;
while(!q.empty()){
int x = q.front(); q.pop(); vis[x] = false;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(e[i].val > 0 && dis[v] > dis[x] + e[i].cost){
dis[v] = dis[x] + e[i].cost;
if(!vis[v])q.push(v), vis[v] = true;
}
}
}
return dis[t] != inf;
}
int dfs(int x, int from){
if(x == t || from <= 0)return from;
vis[x] = true; int res = from;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(!vis[v] && e[i].val > 0 && dis[v] == dis[x] + e[i].cost){
int k = dfs(v, min(res, e[i].val));
e[i].val -= k;
e[i ^ 1].val += k;
res -= k;
if(res <= 0)break;
}
}
return from - res;
}
int flow, cost;
void mcmf(){
while(spfa()){
int k = dfs(s, inf);
flow += k;
cost += k * dis[t];
}
}
void init(){
n = read(), m = read(), K = read(); ++n;
for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)f[i][j] = inf;
for(int i = 1; i <= m; ++i){
int u = read() + 1; int v = read() + 1;
f[u][v] = f[v][u] = min(read(), f[u][v]);
}
for(int i = 1; i <= n; ++i)f[i][i] = 0;
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(k < i || k < j)f[i][j] = min(f[i][k] + f[k][j], f[i][j]);
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j)
if(f[i][j] != inf)link(i, j + n, inf, f[i][j]);
s = n + n + 1, t = s + 1;
link(s, 1, K, 0);
for(int i = 1; i <= n; ++i){
link(i + n, t, 1, 0);
if(i != 1)link(s, i, 1, 0);
}
mcmf();
printf("%d\n",cost);
}
}W;
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
W.init();
return 0;
}
B. Snack
直接建图非常显然,然后考虑模拟最小割的过程优化复杂度
钦定零食连 还是连 ,
则对于每个小孩,连 的代价为
连 的代价为 ,
其中 为零食数量。
那么枚举 , 贪心的选最小的 个割掉
小孩的贡献就是 ,这个可以按 排序后双指针维护。
code
#include<cstdio>
#include<cstring>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll read(){
ll 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 = 300005;
ll n, m, a[maxn];
struct node{
ll b, c;
bool operator < (const node &r)const{
return c * r.b > r.c * b;
}
}d[maxn];
int main(){
n = read(), m = read();
for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = 1; i <= m; ++i)d[i].b = read();
ll sc = 0;
for(int i = 1; i <= m; ++i)d[i].c = read(), sc += d[i].c;
sort(d + 1, d + m + 1); sort(a + 1, a + n + 1);
ll sa = 0, sb = 0, ans = LLONG_MAX;
for(int i = n, j = 1; i >= 0; --i){
sa += a[n - i];
for(; j <= m && d[j].b * i <= d[j].c; ++j)sb += d[j].b, sc -= d[j].c;
ans = min(ans, sa + sb * i + sc);
}
printf("%lld\n",ans);
return 0;
}
C. minimization
绝对值这玩意很难受,考虑在值域上建图,在相同值之间建边来得到代价
如果把数轴画出来,建图就容易多了
每个数的选择都建一条数轴,选择的数前面建流量为选择代价的边
把数轴上数值相同的点都连起来,流量为
然后考虑优化点数,建了很多无用点,实际有用的只有 取值的若干点
把中间的点去掉,与其他数值的流量都放到 那里连
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
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 = 75;
const int maxm = 5e5 + 55;
const int inf = 1e18;
int n, m;
int a[maxn][maxn], c[maxn][maxn], id[maxn][maxn];
struct Dinic{
int s, t, cnt;
int head[maxm], tot = 1;
struct edge{int to, net; ll val;}e[maxm];
void add(int u, int v, int w){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
e[tot].val = w;
}
void link(int u, int v, int w){
if(w)printf("%d %d %d\n",u, v, w);
add(u, v, w); add(v, u, 0);
}
int dep[maxm], now[maxm];
bool bfs(){
memset(dep, 0, sizeof(dep));
queue<int>q; dep[s] = 1; q.push(s);
now[s] = head[s];
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(e[i].val > 0 && dep[v] == 0){
dep[v] = dep[x] + 1;
now[v] = head[v];
if(v == t)return true;
q.push(v);
}
}
}
return false;
}
ll dfs(int x, ll from){
if(x == t || from <= 0)return from;
ll res = from; int i;
for(i = now[x]; i; i = e[i].net){
int v = e[i].to;
if(e[i].val > 0 && dep[v] == dep[x] + 1){
ll k = dfs(v, min(res, e[i].val));
if(k <= 0)dep[v] = 0;
e[i].val -= k;
e[i ^ 1].val += k;
res -= k;
if(res <= 0)break;
}
}
now[x] = i;
return from - res;
}
ll dinic(){
ll ans = 0;
while(bfs())ans += dfs(s, inf);
return ans;
}
void init(){
n = read(), m = read();
s = 1, t = 2, cnt = 2;
for(int i = 1; i <= n; ++i){
id[i][0] = s; id[i][m] = t;
for(int j = 1; j < m; ++j)id[i][j] = ++cnt;
for(int j = 1; j <= m; ++j){
a[i][j] = read(), c[i][j] = read();
link(id[i][j - 1], id[i][j], c[i][j]);
}
a[i][m + 1] = inf;
}
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j){
int w = read();
for(int x = 1; x <= m; ++x)
for(int y = 1; y <= m; ++y){
link(id[i][x - 1], id[j][y], max(0ll, min(a[i][x], a[j][y + 1]) - max(a[i][x - 1], a[j][y])) * w);
link(id[j][x - 1], id[i][y], max(0ll, min(a[j][x], a[i][y + 1]) - max(a[j][x - 1], a[i][y])) * w);
}
}
printf("%lld\n",dinic());
}
}D;
signed main(){
// freopen("minimization.in","r",stdin);
// freopen("minimization.out","w",stdout);
D.init();
return 0;
}
4
A. Manong
非常码农,不想搞
code
B. 字符串
就是对两个字符串集合跑最大匹配
考虑如何在 上做
因为两个串的 上的 就是他们的
所以贪心的在深度深的位置匹配
即能匹配就匹配就是最优的
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 55;
char a[maxn], b[maxn];
int n, k;
struct node{
int ch[26], len, fa;
}d[maxn * 5];
struct SAM{
int cnt = 1, las = 1;
int count[maxn * 5][2];
void ins(int c, int is, int typ){
int fa = las, now = las = ++cnt;
d[now].len = d[fa].len + 1;
count[now][typ] += is;
for(; fa && !d[fa].ch[c]; fa = d[fa].fa) d[fa].ch[c] = now;
if(!fa)d[now].fa = 1;
else{
int j = d[fa].ch[c];
if(d[j].len == d[fa].len + 1)d[now].fa = j;
else{
int clone = ++cnt;
d[clone] = d[j];
d[clone].len = d[fa].len + 1;
d[j].fa = d[now].fa = clone;
for(; fa && d[fa].ch[c] == j; fa = d[fa].fa)d[fa].ch[c] = clone;
}
}
}
int t[maxn], rk[maxn];
void tsp(){
for(int i = 1; i <= cnt; ++i)++t[d[i].len];
for(int i = 1; i <= cnt; ++i)t[i] += t[i - 1];
for(int i = 1; i <= cnt; ++i)rk[t[d[i].len]--] = i;
}
ll solve(){
ll ans=0;
for(int i = cnt; i >= 1; --i){
ll mi = min(count[rk[i]][0],count[rk[i]][1]);
ans += mi * min(d[rk[i]].len, k);
count[rk[i]][0] -= mi; count[rk[i]][1] -= mi;
count[d[rk[i]].fa][0] += count[rk[i]][0];
count[d[rk[i]].fa][1] += count[rk[i]][1];
}
return ans;
}
}S;
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&k);
scanf("%s%s",a + 1,b + 1);
for(int i = n; i >= 1; --i)S.ins(a[i] - 'a', (i + k - 1 <= n), 0);
for(int i = n; i >= 1; --i)S.ins(b[i] - 'a', (i + k - 1 <= n), 1);
S.tsp();
printf("%lld\n",1ll * k * (n - k + 1) - S.solve());
return 0;
}
C. sstr
动态开点线段树维护每个结点的 集合
然后每次先跑匹配,倒着贪心
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 55;
int n, q, m, l, r, top;
struct node{
int fa, len, ch[26];
}d[maxn];
int root[maxn];
struct seg{
int cnt, ls[maxn * 40], rs[maxn * 40];
void ins(int &x, int l, int r, int pos){
if(!x)x = ++cnt;
if(l == r)return;
int mid = (l + r) >> 1;
if(pos <= mid)ins(ls[x], l, mid, pos);
else ins(rs[x], mid + 1, r, pos);
}
int merge(int x, int y, int l, int r){
if(!x || !y)return x | y;
int now = ++cnt;
if(l == r)return now;
int mid = (l + r) >> 1;
ls[now] = merge(ls[x], ls[y], l, mid);
rs[now] = merge(rs[x], rs[y], mid + 1, r);
return now;
}
int query(int x, int l, int r, int L, int R){
if(!x)return 0;
if(L <= l && r <= R)return r - l + 1;
int mid = (l + r) >> 1;
if(L <= mid && R > mid)return query(ls[x], l, mid, L, R) + query(rs[x], mid + 1, r, L, R);
if(L <= mid) return query(ls[x], l, mid, L, R);
else return query(rs[x], mid + 1, r, L, R);
}
}T;
struct SAM{
int cnt = 1, las = 1, pos[maxn];
void ins(int c,int id){
int fa = las, now = las = ++cnt;
d[now].len = d[fa].len + 1;
pos[now] = id; T.ins(root[now], 1, n, id);
for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
if(!fa)d[now].fa = 1;
else{
int j = d[fa].ch[c];
if(d[j].len == d[fa].len + 1)d[now].fa = j;
else{
int clone = ++cnt; d[clone] = d[j];
d[clone].len = d[fa].len + 1;
d[now].fa = d[j].fa = clone;
pos[clone] = id;
for(; fa && d[fa].ch[c] == j; fa = d[fa].fa)d[fa].ch[c] = clone;
}
}
}
vector<int>g[maxn];
void dfs(int x){
for(int v : g[x]){
dfs(v);
root[x] = T.merge(root[x], root[v], 1, n);
}
}
void built(){
for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);
dfs(1);
}
}S;
char s[maxn], c[maxn], p[maxn];
bool dfs(int x, int len){
if(len == m){
for(int i = 0; i < 26; ++i)if(d[x].ch[i] && T.query(root[d[x].ch[i]], 1, n, l + len, r)){
p[++top] = i + 'a';
return true;
}
return false;
}
int val = c[len + 1] - 'a';
if(d[x].ch[val] && T.query(root[d[x].ch[val]], 1, n, l + len, r) && dfs(d[x].ch[val], len + 1)){
p[++top] = c[len + 1]; return true;
}
for(int i = val + 1; i < 26; ++i)if(d[x].ch[i] && T.query(root[d[x].ch[i]], 1, n, l + len, r)){
p[++top] = i + 'a';
return true;
}
return false;
}
void solve(){
m = strlen(c + 1);
top = 0;
if(dfs(1, 0)){
for(int i = top; i >= 1; --i)putchar(p[i]);putchar('\n');
}else printf("-1\n");
}
int main(){
// freopen("sstr.in","r",stdin);
// freopen("sstr.ans","w",stdout);
scanf("%s",s + 1);
n = strlen(s + 1);
for(int i = 1; i <= n; ++i)S.ins(s[i] - 'a', i);
S.built();
scanf("%d",&q);
cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
for(int i = 1; i <= q; ++i){
scanf("%d%d%s",&l, &r, c + 1);
solve();
cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
}
return 0;
}
5
真的找不到题了,考的题我找题时候做过俩。。。
A. Light
枚举 的长度 , 每 个位置设一个关键点
两个 必然都过关键点
从一个关键点 可以得到对应的匹配的位置
找到两者的 确定范围
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 = 2e5 + 55;
int n, g;
char c[maxn];
struct SA{
int s[maxn];
int ht[maxn];
int sa[maxn], id[maxn], px[maxn], cnt[maxn], rk[maxn << 1], ork[maxn << 1 | 1];
bool cmp(int x, int y, int w){return ork[x] == ork[y] && ork[x + w] == ork[y + w];}
int st[maxn][19];
int n;
void clear(){
for(int i = 1; i <= n; ++i)sa[i] = rk[i] = ht[i] = 0;
for(int i = 0; (1 << i) <= n; ++i)
for(int j = 1; j + (1 << i) - 1 <= n; ++j)
st[j][i] = 0;
}
void get_sa(){
int m = max(n, 300);
for(int i = 1; i <= m; ++i)cnt[i] = 0;
for(int i = 1; i <= n; ++i)++cnt[rk[i] = s[i]];
for(int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1];
for(int i = n; i >= 1; --i)sa[cnt[rk[i]]--] = i;
for(int w = 1, p; w <= n; w <<= 1){
p = 0;
for(int i = n; i > n - w; --i)id[++p] = i;
for(int i = 1; i <= n; ++i)if(sa[i] > w)id[++p] = sa[i] - w;
for(int i = 1; i <= m; ++i)cnt[i] = 0;
for(int i = 1; i <= n; ++i)++cnt[px[i] = rk[id[i]]];
for(int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1];
for(int i = n; i >= 1; --i)sa[cnt[px[i]]--] = id[i];
for(int i = 1; i <= n; ++i)ork[i] = rk[i];
p = 0;
for(int i = 1; i <= n; ++i)rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
if(p == n)break;
m = p;
}
}
void get_ht(){
for(int i = 1, k = 0; i <= n; ++i){
if(rk[i] == 1)continue;
if(k)--k;
while(s[i + k] == s[sa[rk[i] - 1] + k])++k;
ht[rk[i]] = k;
}
}
void get_st(){
for(int i = 1; i <= n; ++i)st[i][0] = ht[i];
for(int j = 1; (1 << j) <= n; ++j)
for(int i = 1; i + (1 << j) - 1 <= n; ++i)
st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
void init(){
get_sa();
get_ht();
get_st();
}
int query(int l, int r){
if(l == r)return n - sa[l] + 1;
if(l > r)swap(l, r); ++l;
int k = log2(r - l + 1);
return min(st[l][k], st[r - (1 << k) + 1][k]);
}
}LCP, LCS;
void sol(){
scanf("%d%s",&g, c + 1);
n = strlen(c + 1);
LCP.clear(); LCS.clear();
LCP.n = LCS.n = n;
for(int i = 1; i <= n; ++i)LCP.s[i] = LCS.s[n - i + 1] = c[i] - 'a' + 1;
LCP.init(); LCS.init();
int ans = 0;
for(int len = 1; len <= n; ++len){
for(int i = 1; i <= n; i += len){
int l = i, r = i + len + g;
if(r > n)break;
int R = n - l + 1, L = n - r + 1;
int S = min(len, LCS.query(LCS.rk[L], LCS.rk[R]));
int P = min(len, LCP.query(LCP.rk[l], LCP.rk[r]));
int res = S + P - len + (S == 0 || P == 0);
ans = ans + max(0, res);
}
}
printf("%d\n",ans);
}
int main(){
// freopen("light.in","r",stdin);
// freopen("light.ans","w",stdout);
int t; scanf("%d",&t);
for(int i = 1; i <= t; ++i)sol();
return 0;
}
B. 发烧
启发式合并维护当前节点在哪些串出现过,然后直接跑匹配
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 500005;
struct node{
int fa, len, ch[26];
}d[maxn];
int n, k;
string s[maxn];
set<int>st[maxn];
vector<int>g[maxn];
int num[maxn];
struct SAM{
int las = 1, cnt = 1;
void ins(int c, int pos){
if(d[las].ch[c]){
int fa = las, x = d[las].ch[c];
if(d[fa].len + 1 == d[x].len)las = x;
else{
int now = las = ++cnt;
d[now] = d[x];
d[now].len = d[fa].len + 1;
d[x].fa = now;
for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = now;
}
}else{
int fa = las, now = las = ++cnt; d[now].len = d[fa].len + 1;
for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
if(!fa)d[now].fa = 1;
else{
int x = d[fa].ch[c];
if(d[fa].len + 1 == d[x].len)d[now].fa = x;
else{
int clone = ++cnt;
d[clone] = d[x];
d[clone].len = d[fa].len + 1;
d[now].fa = d[x].fa = clone;
for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
}
}
}
st[las].insert(pos);
}
void merge(set<int>&x, set<int>&y){
if(x.size() < y.size())swap(x, y);
for(int v : y)x.insert(v);
y.clear();
}
void dfs(int x){
for(int v : g[x]){
dfs(v);
merge(st[x], st[v]);
}
num[x] = st[x].size();
}
void built(){
for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);
dfs(1);
}
}S;
ll sol(int id){
int len = s[id].size(), now = 1;
ll ans = 0;
for(int i = 0; i < len; ++i){
int c = s[id][i] - 'a';
while(now != 1 && !d[now].ch[c])now = d[now].fa;
if(d[now].ch[c])now = d[now].ch[c];
while(now != 1 && num[now] < k)now = d[now].fa;
if(num[now] >= k)ans += d[now].len;
}
return ans;
}
int main(){
freopen("fever.in","r",stdin);
freopen("fever.out","w",stdout);
cin >> n >> k;
for(int i = 1; i <= n; ++i){
cin >> s[i];
int len = s[i].size();
S.las = 1;
for(int j = 0; j < len; ++j)S.ins(s[i][j] - 'a', i);
}
S.built();
for(int i = 1; i <= n; ++i)cout << sol(i) << " ";cout << endl;
return 0;;
}
C. Big_Water_Problem
建广义 , 分别维护两个串的 集合大小
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 500005;
struct node{
int fa, len, ch[26];
}d[maxn];
char s[maxn], c[maxn];
int n, m;
vector<int>g[maxn];
struct SAM{
int si[maxn][2];
int las = 1, cnt = 1;
void ins(int c, int id){
if(d[las].ch[c]){
int fa = las, x = d[las].ch[c];
if(d[fa].len + 1 == d[x].len)las = x;
else{
int now = las = ++cnt;
d[now] = d[x];
d[now].len = d[fa].len + 1;
d[x].fa = now;
for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = now;
}
}else{
int fa = las, now = las = ++cnt; d[now].len = d[fa].len + 1;
for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
if(!fa)d[now].fa = 1;
else{
int x = d[fa].ch[c];
if(d[fa].len + 1 == d[x].len)d[now].fa = x;
else{
int clone = ++cnt;
d[clone] = d[x];
d[clone].len = d[fa].len + 1;
d[now].fa = d[x].fa = clone;
for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
}
}
}
++si[las][id];
}
void dfs(int x){
for(int v : g[x]){
dfs(v);
si[x][0] += si[v][0];
si[x][1] += si[v][1];
}
}
int solve(){
for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);
dfs(1);
int mi = 0x3f3f3f3f;
for(int i = 2; i <= cnt; ++i)if(si[i][0] == 1 && si[i][1] == 1)mi = min(mi, d[d[i].fa].len + 1);
return mi == 0x3f3f3f3f ? -1 : mi;
}
}S;
int main(){
freopen("Big_Water_Problem.in","r",stdin);
freopen("Big_Water_Problem.out","w",stdout);
scanf("%s%s",s + 1, c + 1);
n = strlen(s + 1); m = strlen(c + 1);
for(int i = 1; i <= n; ++i)S.ins(s[i] - 'a', 0);
S.las = 1;
for(int i = 1; i <= m; ++i)S.ins(c[i] - 'a', 1);
printf("%d\n",S.solve());
return 0;;
}
6
CF755G / 小组学习
设 为考虑前 个球,取了 组的方案数
显然有
如果把当前分组拆成两部分,分拼接部分是一组还是两组考虑
那么考虑倍增
为了方便先写成多项式 为了省事下面不写
所以可以搞了,具体来讲就是按照二进制位考虑,每次左移或者加一
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 131075;
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;
}
int deg, len, rev[maxn], wn[maxn];
void init(){
deg = 1; while(deg < len)deg <<= 1;
wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
for(int i = 1; i < deg; ++i){
rev[i] = rev[i >> 1] >> 1;
if(i & 1)rev[i] |= (deg >> 1);
wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
}
}
struct poly{
int f[maxn];
int &operator [] (const int &i){return f[i];}
void ntt(){
for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1){
for(int i = 0; i < deg; i += l){
int w = 1;
for(int j = i; j < i + hl; ++j){
int x = f[j], y = 1ll * w * f[j + hl] % mod;
f[j] = (x + y) % mod; f[j + hl] = (0ll + x - y + mod) % mod;
w = 1ll * w * wn[deg / l] % mod;
}
}
}
}
void intt(){
ntt(); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
}
}f[3], g[5];
int n, k;
void mul(){
f[0].ntt(); f[1].ntt(); f[2].ntt();
for(int i = 0; i < deg; ++i)g[0][i] = g[1][i] = g[2][i] = g[3][i] = g[4][i] = 0;
for(int i = 0; i < deg; ++i){
g[0][i] = 1ll * f[0][i] * f[0][i] % mod;
g[1][i] = 1ll * f[1][i] * f[1][i] % mod;
g[2][i] = 1ll * f[2][i] * f[2][i] % mod;
g[3][i] = 1ll * f[0][i] * f[1][i] % mod;
g[4][i] = 1ll * f[1][i] * f[2][i] % mod;
}
g[0].intt(); g[1].intt(); g[2].intt(); g[3].intt(); g[4].intt();
for(int i = 0; i <= k; ++i){
f[0][i] = (g[0][i] + (i ? g[1][i - 1] : 0)) % mod;
f[1][i] = (g[3][i] + (i ? g[4][i - 1] : 0)) % mod;
f[2][i] = (g[1][i] + (i ? g[2][i - 1] : 0)) % mod;
}
for(int i = k + 1; i < deg; ++i)f[0][i] = f[1][i] = f[2][i] = 0;
}
void add(){
for(int i = 0; i <= k; ++i)f[2][i] = f[1][i], f[1][i] = f[0][i]; f[0][0] = 1;
for(int i = 1; i <= k; ++i)f[0][i] = (0ll + f[1][i] + f[1][i - 1] + f[2][i - 1]) % mod;
}
int main(){
// freopen("group.in","r",stdin);
// freopen("group.ans","w",stdout);
scanf("%d%d",&n,&k); len = k + k + 5;
f[0][0] = f[0][1] = f[1][0] = 1; init();
for(int i = log2(n) - 1; i >= 0; --i){mul();if(n & (1 << i))add();}
for(int i = 1; i <= k; ++i)printf("%d ",f[0][i]);
return 0;
}
CF623E Transforming Sequence
设 表示前 个数,或值有 位为 , (只知道他们的相对大小关系,最后乘上组合数即可)
有
可得
然后可以倍增,过程中维护 方便处理
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 131075;
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;
}
int deg, len, rev[maxn], wn[maxn];
void init(){
deg = 1; while(deg < len)deg <<= 1;
wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
for(int i = 1; i < deg; ++i){
rev[i] = rev[i >> 1] >> 1;
if(i & 1)rev[i] |= (deg >> 1);
wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
}
}
struct poly{
int f[maxn];
int &operator [] (const int &i){return f[i];}
void ntt(){
for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1){
for(int i = 0; i < deg; i += l){
int w = 1;
for(int j = i; j < i + hl; ++j){
int x = f[j], y = 1ll * w * f[j + hl] % mod;
f[j] = (x + y) % mod; f[j + hl] = (x - y + mod) % mod;
w = 1ll * w * wn[deg / l] % mod;
}
}
}
}
void intt(){
ntt(); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
}
}f, g;
int k;
int fac[maxn], inv[maxn];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
void solve(int n){
if(n == 1){
f[0] = 0; for(int i = 1; i <= k; ++i)f[i] = inv[i];
return;
}
solve(n >> 1);
g[0] = 0; for(int i = 1; i <= k; ++i)g[i] = 1ll * f[i] * qpow(2, 1ll * (n >> 1) * i % (mod - 1)) % mod;
for(int i = k + 1; i < deg; ++i)f[i] = g[i] = 0;
f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod; f.intt();
if(n & 1){
g[0] = 0;
for(int i = 1; i <= k; ++i)f[i] = 1ll * f[i] * qpow(2, i) % mod, g[i] = inv[i];
for(int i = k + 1; i < deg; ++i)f[i] = g[i] = 0;
f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod; f.intt();
}
}
ll n;
int main(){
// freopen("ts.in","r",stdin);
// freopen("ts.ans","w",stdout);
scanf("%lld%d",&n,&k);
if(n > k){printf("0\n");return 0;}
fac[0] = inv[0] = 1; for(int i = 1; i <= k; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
inv[k] = qpow(fac[k], mod - 2); for(int i = k - 1; i >= 1; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
len = k + k + 10; init();
solve(n);
int ans = 0;
for(int i = 1; i <= k; ++i)ans = (ans + 1ll * f[i] * fac[k] % mod * inv[k - i] % mod ) % mod;
printf("%d\n",ans);
return 0;
}
树
设 表示考虑 的子树,选择了 异或为 的方案数
转移发现就是异或卷积卷上
code
#include<bits/stdc++.h>
using namespace std;
int read(){
int x = 0; bool f = 0; char c = getchar();
while(!isdigit(c)){f = c == '-'; c = getchar();}
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return f ? -x : x;
}
const int maxn = 2505;
const int mod = 1e9 + 7;
const int inv2 = (mod + 1) / 2;
int n, m, v[maxn], fa[maxn];
void fwt_xor(int f[], int opt){
for(int l = 2, k = 1; l <= m; l <<= 1, k <<= 1)
for(int i = 0; i < m; i += l)
for(int j = 0; j < k; ++j){
int x = f[i + j], y = f[i + j + k];
f[i + j] = 1ll * opt * (x + y) % mod;
f[i + j + k] = 1ll * opt * (x - y + mod) % mod;
}
}
vector<int>g[maxn];
int f[maxn][maxn], ans[maxn];
void dfs(int x){
f[x][v[x]] = 1; fwt_xor(f[x], 1);
for(int v : g[x])if(v != fa[x]){
fa[v] = x; dfs(v);
for(int i = 0; i < m; ++i)f[x][i] = 1ll * f[x][i] * f[v][i] % mod;
}
fwt_xor(f[x], inv2);
for(int i = 0; i < m; ++i)ans[i] = (ans[i] + f[x][i]) % mod;
++f[x][0]; fwt_xor(f[x], 1);
}
int main(){
n = read(), m = read();
for(int i = 1; i <= n; ++i)v[i] = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
g[u].push_back(v); g[v].push_back(u);
}
dfs(1);
for(int i = 0; i < m; ++i)printf("%d ",ans[i]);
return 0;
}
7
A. tree / 无聊的水题 I
序列与有标号无根树一一对应
问题变成求多少 序列中有元素出现 次,并且没有出现次数更多的
转化成至多 次 - 至多 次
假设我们知道每个数出现多少次,那么答案就是
于是设指数型生成函数
取 即为所求
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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 = 400005;
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;
}
int rev[maxn], wn[maxn], deg;
void init(int len){
deg = 1; while(deg < len)deg <<= 1;
wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
for(int i = 1; i < deg; ++i){
wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
rev[i] = rev[i >> 1] >> 1;
if(i & 1)rev[i] |= (deg >> 1);
}
}
int add(int x, int y){x += y; return x >= mod ? x - mod : x;}
struct poly{
int f[maxn];
int &operator [](const int &i){return f[i];}
int operator [](const int &i)const{return f[i];}
void ntt(){
for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
for(int i = 0; i < deg; i += l){
int w = 1, x, y;
for(int j = i; j < i + hl; ++j){
x = f[j], y = 1ll * w * f[j + hl] % mod;
f[j] = add(x, y); f[j + hl] = add(x, mod - y);
w = 1ll * w * wn[deg / l] % mod;
}
}
}
void intt(){
ntt(); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
}
void del(int l){for(int i = l; i < deg; ++i)f[i] = 0;}
void operator *= (const poly x){
ntt(); poly a = x; a.ntt();
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * a[i] % mod;
intt();
}
}f, g;
int n, m, fac[maxn], ifac[maxn];
int sol(int mx){
init(n + n);
for(int i = 0; i <= mx; ++i)g[i] = ifac[i];
for(int i = mx + 1; i < deg; ++i)g[i] = 0;
for(int i = 1; i < deg; ++i)f[i] = 0; f[0] = 1;
int y = n; for(; y; y >>= 1, g *= g, g.del(n - 1))if(y & 1)f *= g, f.del(n - 1);
return 1ll * f[n - 2] * fac[n - 2] % mod;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n = read(), m = read();
fac[0] = ifac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
printf("%d\n",(sol(m - 1) - sol(m - 2) + mod) % mod);
return 0;
}
B. 残缺的字符串
反转一个,匹配变成卷积形式
如果字符集小,可以枚举字符设成 ,直接乘
但是 的常数足够 飞了
把 换掉,变成
把通配符设成
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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 = 2000005;
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;
}
int rev[maxn], wn[maxn], deg;
void init(int len){
deg = 1; while(deg < len)deg <<= 1;
wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
for(int i = 1; i < deg; ++i){
wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
rev[i] = rev[i >> 1] >> 1;
if(i & 1)rev[i] |= (deg >> 1);
}
}
int add(int x, int y){x += y; return x >= mod ? x - mod : x;}
struct poly{
int f[maxn];
int &operator [](const int &i){return f[i];}
void ntt(){
for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
for(int i = 0; i < deg; i += l){
int w = 1, x, y;
for(int j = i; j < i + hl; ++j){
x = f[j], y = 1ll * w * f[j + hl] % mod;
f[j] = add(x, y); f[j + hl] = add(x, mod - y);
w = 1ll * w * wn[deg / l] % mod;
}
}
}
void intt(){
ntt(); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
}
}f, g;
int n, m, h[maxn];
char s[maxn], t[maxn];
int a[maxn], b[maxn];
void solve(char ch){
for(int i = 0; i < deg; ++i)f[i] = (s[i] == ch || s[i] == '*');
for(int i = 0; i < deg; ++i)g[i] = (t[i] == ch || t[i] == '*');
f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
f.intt();
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&m); init(n + m + 1);
scanf("%s%s",s,t); reverse(s, s + n);
for(int i = 0; i < n; ++i)a[i] = s[i] == '*' ? 0 : s[i] - 'a' + 1;
for(int i = 0; i < m; ++i)b[i] = t[i] == '*' ? 0 : t[i] - 'a' + 1;
for(int i = 0; i < deg; ++i)f[i] = a[i] * a[i] * a[i], g[i] = b[i];
f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
f.intt(); for(int i = 0; i < deg; ++i)h[i] = f[i];
for(int i = 0; i < deg; ++i)f[i] = a[i] * a[i], g[i] = b[i] * b[i];
f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
f.intt(); for(int i = 0; i < deg; ++i)h[i] = (h[i] - 2ll * f[i]) % mod;
for(int i = 0; i < deg; ++i)f[i] = a[i], g[i] = b[i] * b[i] * b[i];
f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
f.intt(); for(int i = 0; i < deg; ++i)h[i] = (h[i] + f[i]) % mod;
int cnt = 0;
for(int j = 0; j < m - n + 1; ++j)if(h[j + n - 1] == 0) ++cnt;
printf("%d\n",cnt);
if(cnt){for(int j = 0; j < m - n + 1; ++j)if(h[j + n - 1] == 0)printf("%d ",j + 1);printf("\n");}
return 0;
}
序列 / ARC145F Modulo Sum of Increasing Sequences
咕咕咕咕
https://www.cnblogs.com/apjifengc/p/17016937.html
code
8
A. 保护出题人
前缀和出来,发现每次求
可以看成 到 的斜率
发现定点在点集的右上方,画一下图是维护下凸包
每次二分凸包即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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 = 1000005;
int n, d, top;
ll a[maxn], b[maxn];
struct node{ll x, y;}st[maxn];
double slope(node x, node y){return (double)(x.y - y.y) / (double)(x.x - y.x);}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n = read(), d = read();
for(int i = 1; i <= n; ++i){a[i] = read(), b[i] = read();}
for(int i = 1; i <= n; ++i)a[i] += a[i - 1];
double ans = 0;
for(int i = 1; i <= n; ++i){
node now = {1ll * i * d, a[i - 1]};
while(top > 1 && slope(st[top], st[top - 1]) > slope(now, st[top]))--top;
st[++top] = now; now = {b[i] + 1ll * i * d, a[i]};
int l = 1, r = top; double res = 0;
while(l <= r){
if(r - l <= 5){
for(int j = l; j <= r; ++j)
res = max(res, slope(now, st[j]));
break;
}
int mid = (l + r) >> 1;
if(slope(now, st[mid]) > slope(now, st[mid + 1]))r = mid;
else l = mid;
}
ans += res;
}
printf("%.0lf\n",ans);
return 0;
}
B. 收集 / UVA1606 两亲性分子 Amphiphilic Carbon Molecules
枚举两个点确定直线,直接搞
考虑确定一个点,然后按照极角排序,那么可以双指针确定选择点的范围
维护一下有多少点即可
为了方便,在处理的时候把一种颜色的点坐标取反,这样只用看一侧的点
code
// #include<bits/stdc++.h>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
int read(){
int x = 0; bool f = false; char c = getchar();
while(!isdigit(c)){f = c == '-'; c = getchar();}
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return f ? -x : x;
}
const int maxn = 1005;
struct point{int x, y; bool col;}p[maxn];
struct ver{
double x, y, deg;
friend bool operator < (const ver &x, const ver &y){return x.deg < y.deg;}
}d[maxn];
double cross(const ver &x, const ver &y){
return x.x * y.y - x.y * y.x;
}
int n;
int solve(){
if(n <= 2)return n;
int ans = 0;
for(int i = 1; i <= n; ++i){
int cnt = 0;
for(int j = 1; j <= n; ++j)if(i != j){
d[++cnt].x = p[j].x - p[i].x;
d[cnt].y = p[j].y - p[i].y;
if(p[j].col)d[cnt].x = -d[cnt].x, d[cnt].y = -d[cnt].y;
d[cnt].deg = atan2(d[cnt].y, d[cnt].x);
}
sort(d + 1, d + cnt + 1);
int l = 1, r = 1, res = 2;
while(l <= cnt){
if(l == r)(r = r == cnt ? 1 : r + 1), ++res;
while(l != r && cross(d[l], d[r]) >= 0){
r = r == cnt ? 1 : r + 1;
++res;
}
++l; --res;
ans = max(ans, res);
}
}
return ans;
}
int main(){
freopen("collect.in","r",stdin);
freopen("collect.out","w",stdout);
n = read();
while(n){
for(int i = 1; i <= n; ++i)p[i].x = read(), p[i].y = read(), p[i].col = read();
printf("%d\n",solve());
n = read();
}
return 0;
}
C. 逃离包围圈 / CF932F Escape Through Leaf
发现是形如 的函数求最值,所以李超线段树解决
因为在树上,需要子树们的信息,所以进行李超线段树合并
做法在 时把一个点信息插入另外一个,再递归左右儿子
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar(); bool f = false;
while(!isdigit(c)){f = c == '-'; c = getchar();}
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return f ? -x : x;
}
const int maxn = 500005;
const ll inf = 1e18;
const int lim = 1e5;
int n, a[maxn], b[maxn], fa[maxn];
ll f[maxn];
vector<int>g[maxn];
struct line{ll k, b;}li[maxn];
ll calc(int id, int val){return 1ll * li[id].k * val + li[id].b;}
int tot;
int root[maxn];
struct lct{
struct node{
int id, l, r;
}t[maxn << 2 | 1];
int cnt;
void insert(int &x, int l, int r, int id){
if(!x){t[x = ++cnt].id = id; return;}
int mid = (l + r) >> 1;
ll v1 = calc(id, mid), v2 = calc(t[x].id, mid);
if(v1 < v2)swap(id, t[x].id);
if(l == r)return;
if(calc(id, l) < calc(t[x].id, l))insert(t[x].l, l, mid, id);
if(calc(id, r) < calc(t[x].id, r))insert(t[x].r, mid + 1, r, id);
}
int merge(int x, int y, int l, int r){
if(!x || !y)return x | y;
insert(x, l, r, t[y].id);
int mid = (l + r) >> 1;
t[x].l = merge(t[x].l, t[y].l, l, mid);
t[x].r = merge(t[x].r, t[y].r, mid + 1, r);
return x;
}
ll query(int x, int l, int r, int val){
if(!x)return inf;
ll ans = calc(t[x].id, val);
if(l == r)return ans;
int mid = (l + r) >> 1;
if(val <= mid)ans = min(ans, query(t[x].l, l, mid, val));
else ans = min(ans, query(t[x].r, mid + 1, r, val));
return ans;
}
}T;
void dfs(int x){
bool noleaf = false;
for(int v : g[x])if(v != fa[x]){
fa[v] = x; noleaf = true;
dfs(v);
root[x] = T.merge(root[x], root[v], -lim, lim);
}
if(noleaf)f[x] = T.query(root[x], -lim, lim, a[x]);
li[++tot] = {b[x], f[x]};
T.insert(root[x], -lim, lim, tot);
}
signed main(){
freopen("escape.in","r",stdin);
freopen("escape.ans","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = 1; i <= n; ++i)b[i] = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
g[u].push_back(v); g[v].push_back(u);
}
dfs(1);
for(int i = 1; i <= n; ++i)printf("%lld ",f[i]); printf("\n");
return 0;
}
9
A. Delov的魔力
草,之前说要再做一个 的题,实在找不到拿来凑数的今天考了。。。。
就是求 , 然后特别考虑一下没有相同字符的,跑全源最短路
code
#include<queue>
#include<iostream>
#include<cstring>
#include<map>
#include<climits>
using namespace std;
int read(){
int x = 0; bool f = 0; char c = getchar();
while(!isdigit(c)){f = c == '-'; c = getchar();}
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return f ? -x : x;
}
const int maxn = 200005;
int n;
char c[maxn];
struct node{
int fa, len;
bool vis[3];
map<int, int>ch;
}d[maxn << 4 | 1];
struct SAM{
int las = 1, cnt = 1;
void insert(int c, int str){
if(d[las].ch[c]){
int fa = las, x = d[las].ch[c];
if(d[fa].len + 1 == d[x].len)las = x;
else{
int now = las = ++cnt;
d[now] = d[x];
d[now].len = d[fa].len + 1;
for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = now;
d[x].fa = now;
}
}else{
int fa = las;
int now = las = ++cnt;
d[now].len = d[fa].len + 1;
for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
if(!fa)d[now].fa = 1;
else{
int x = d[fa].ch[c];
if(d[fa].len + 1 == d[x].len)d[now].fa = x;
else{
int clone = ++cnt;
d[clone] = d[x];
d[clone].len = d[fa].len + 1;
d[x].fa = d[now].fa = clone;
for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
}
}
}
d[las].vis[str] = 1;
}
queue<int> q;
int rd[maxn << 4 | 1];
int work(){
for(int i = 2; i <= cnt; ++i)++rd[d[i].fa];
for(int i = 2; i <= cnt; ++i)if(rd[i] == 0)q.push(i);
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = 1; i <= n; ++i)d[d[x].fa].vis[i] += d[x].vis[i];
--rd[d[x].fa];
if(rd[d[x].fa] == 0)q.push(d[x].fa);
}
int ans = 0;
for(int i = 2; i <= cnt; ++i){
for(int j = 1; j <= 2; ++j){
if(d[i].vis[j] == 0)break;
if(j == 2)ans = max(ans, d[i].len);
}
}
return ans;
}
}S;
int a[maxn], s[maxn], t[maxn], la, lb;
vector<int>g[maxn];
int dis[maxn];
queue<int>q;
int sol(){
for(int i = 1; i < n; ++i)g[a[i]].push_back(a[i + 1]), g[a[i + 1]].push_back(a[i]);
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= la; ++i)dis[s[i]] = 0, q.push(s[i]);
while(!q.empty()){
int x = q.front(); q.pop();
for(int v : g[x]){
if(dis[v] > dis[x] + 1){dis[v] = dis[x] + 1; q.push(v);}
}
}
int ans = dis[0];
for(int i = 1; i <= lb; ++i)ans = min(ans, dis[t[i]]);
return la + lb + ans + ans - 2;
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
cin >> n; for(int i = 1; i <= n; ++i)a[i] = read();
S.las = 1; la = read(); for(int i = 1; i <= la; ++i)S.insert(s[i] = read(), 1);
S.las = 1; lb = read(); for(int i = 1; i <= lb; ++i)S.insert(t[i] = read(), 2);
int lcs = S.work();
if(lcs)printf("%d\n",la - lcs + lb - lcs);
else printf("%d\n",sol());
return 0;
}
B. Mogohu-Rea Idol
结论题,
求三个凸包的闵可夫斯基和,判断点是否在凸包内即可
code
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; bool f = false; char c = getchar();
while(!isdigit(c)){f = c == '-'; c = getchar();}
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return f ? -x : x;
}
const int maxn = 600005;
const double eps = 1e-8;
int zfx(double x){return x > eps ? 1 : x < -eps ? -1 : 0;}
struct point{
double x, y;
void rd(){x = read(), y = read();}
friend bool operator <(const point &x, const point &y){return zfx(x.x - y.x) ? x.x < y.x : x.y < y.y;}
friend point operator + (const point &x, const point &y){return point{x.x + y.x, x.y + y.y};}
friend point operator - (const point &x, const point &y){return point{x.x - y.x, x.y - y.y};}
friend point operator * (const point &x, const double y){return point{x.x * y, x.y * y};}
}p0[maxn], p1[maxn], p2[maxn], p3[maxn], res[maxn], v1[maxn], v2[maxn];
double cha(point a, point b){return a.x * b.y - a.y * b.x;}
double shu(point a, point b){return a.x * b.x + a.y * b.y;}
bool online(point p, point a, point b){
return !zfx(cha(p - a, p - b)) && zfx(shu(p - a, p - b)) < 0;
}
int n1, n2, n3, n;
int tb(point p[], int n, point ans[]){
sort(p + 1, p + n + 1);
int top = 0;
for(int i = 1; i <= n; ++i){
while(top > 1 && zfx(cha(ans[top] - ans[top - 1], p[i] - ans[top - 1]) <= 0))--top;
ans[++top] = p[i];
}
int t = top;
for(int i = n - 1; i >= 1; --i){
while(top > t && zfx(cha(ans[top] - ans[top - 1], p[i] - ans[top - 1]) <= 0))--top;
ans[++top] = p[i];
}
return --top;
}
int mkadd(point p_1[], int n1, point p_2[], int n2, point ans[]){
for(int i = 1; i <= n1; ++i)v1[i] = p_1[i < n1 ? i + 1 : 1] - p_1[i];
for(int i = 1; i <= n2; ++i)v2[i] = p_2[i < n2 ? i + 1 : 1] - p_2[i];
int top = 0, p1 = 1, p2 = 1; ans[++top] = p_1[1] + p_2[1];
while(p1 <= n1 && p2 <= n2)++top, ans[top] = ans[top - 1] + (zfx(cha(v1[p1], v2[p2])) > 0 ? v1[p1++] : v2[p2++]);
while(p1 <= n1)++top, ans[top] = ans[top - 1] + v1[p1++];
while(p2 <= n2)++top, ans[top] = ans[top - 1] + v2[p2++];
return top;
}
int pip(point p[], int n, point now){
if(zfx(cha(now - p[1], p[2] - p[1])) > 0 || zfx(cha(p[n] - p[1], now - p[1])) > 0)return 0;
if(online(now, p[1], p[2]) || online(now, p[1], p[n]))return 1;
int l = 2, r = n - 1;
while(l < r){
int mid = l + r + 1 >> 1;
if(zfx(cha(now - p[1], p[mid] - p[1])) > 0)r = mid - 1;
else l = mid;
}
return zfx(cha(now - p[r], p[r + 1] - p[r])) <= 0;
}
int main(){
freopen("idol.in","r",stdin);
freopen("idol.out","w",stdout);
n1 = read(); for(int i = 1; i <= n1; ++i)p1[i].rd();
n2 = read(); for(int i = 1; i <= n2; ++i)p2[i].rd();
n3 = read(); for(int i = 1; i <= n3; ++i)p3[i].rd();
n1 = tb(p1, n1, p0); for(int i = 1; i <= n1; ++i)p1[i] = p0[i];
n2 = tb(p2, n2, p0); for(int i = 1; i <= n2; ++i)p2[i] = p0[i];
n3 = tb(p3, n3, p0); for(int i = 1; i <= n3; ++i)p3[i] = p0[i];
n = mkadd(p1, n1, p2, n2, p0); n = mkadd(p0, n, p3, n3, res);
n = tb(res, n, p0);
int t = read();
for(int i = 1; i <= t; ++i){
point now; now.rd();
if(pip(p0, n, now * 3))printf("YES\n");
else printf("NO\n");
}
return 0;
}
C. 墙
现场学习 , 判断每个点是否在某些多边形内即可
code
#include<bits/stdc++.h>
using namespace std;
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 = 2005;
int n, m;
struct wall{int a, b, c, d;}w[maxn];
map<pii, int>mp;
map<int, int>id;
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]);}
void merge(int x, int y){f[fa(y)] = fa(x);}
}S;
vector<int>poly[maxn];
int cnt;
map<vector<bool>, int>res;
vector<bool>op[maxn];
bool check(int x, int y, int id){
int res = 0;
for(int i = 0; i < poly[id].size(); ++i){
wall now = w[poly[id][i]];
if(y >= min(now.b, now.d) && y < max(now.b, now.d)){
res += (now.a + (double)(y - now.b) / (double)(now.d - now.b) * (now.c - now.a) - x > 1e-10);
}
}
return res & 1;
}
int main(){
freopen("wall.in","r",stdin);
freopen("wall.out","w",stdout);
n = read(), m = read();
S.init();
for(int i = 1; i <= n; ++i){
w[i].a = read(); w[i].b = read();
if(mp[pii(w[i].a, w[i].b)])S.merge(i, mp[pii(w[i].a, w[i].b)]);
else mp[pii(w[i].a, w[i].b)] = i;
w[i].c = read(); w[i].d = read();
if(mp[pii(w[i].c, w[i].d)])S.merge(i, mp[pii(w[i].c, w[i].d)]);
else mp[pii(w[i].c, w[i].d)] = i;
}
for(int i = 1; i <= n; ++i){
if(!id[S.fa(i)])id[S.fa(i)] = ++cnt;
poly[id[S.fa(i)]].push_back(i);
}
for(int i = 1; i <= m; ++i){
int x = read(), y = read();
for(int j = 1; j <= cnt; ++j)op[i].push_back(check(x, y, j));
}
int ans = 0;
for(int i = 1; i <= m; ++i)++res[op[i]], ans = max(ans, res[op[i]]);
printf("%d\n",ans);
return 0;
}
专项测试
字符串1
A. 回文子串
线段树维护一个位置开始/结尾的在 范围内的子串
每次暴力重构两边,中间区间覆盖
发现字母也需要区间覆盖,则再开一棵树维护字母
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 50005;
int n, k, q;
char s[maxn];
char c[maxn << 1 | 1];
int m, p[maxn << 1 | 1], cnt[maxn << 1 | 1];
struct seg{
struct node{
char ch;
int tag, laz, len, sum;
}t[maxn << 2 | 1];
void push_up(int x){t[x].sum = t[x << 1].sum + t[x << 1 | 1].sum;}
void upd(int x, int val){t[x].sum = val * t[x].len; t[x].tag = val;}
void upc(int x, char c){t[x].laz = t[x].ch = c;}
void push_down(int x){
if(t[x].tag != -1)upd(x << 1, t[x].tag), upd(x << 1 | 1, t[x].tag);
if(t[x].laz != -1)upc(x << 1, t[x].laz), upc(x << 1 | 1, t[x].laz);
t[x].laz = t[x].tag = -1;
}
void built(int x, int l, int r){
t[x].tag = t[x].laz = -1; t[x].len = r - l + 1;
if(l == r){ t[x].ch = s[l]; return;}
int mid = (l + r) >> 1;
built(x << 1, l, mid);
built(x << 1 | 1, mid + 1, r);
}
void modify(int x, int l, int r, int L, int R, int val){
if(L <= l && r <= R){ upd(x, val); return;}
push_down(x);
int mid = (l + r) >> 1;
if(L <= mid)modify(x << 1, l, mid, L, R, val);
if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
push_up(x);
}
void update(int x, int l, int r, int L, int R, char c){
if(L <= l && r <= R){ upc(x, c); return;}
push_down(x);
int mid = (l + r) >> 1;
if(L <= mid)update(x << 1, l, mid, L, R, c);
if(R > mid)update(x << 1 | 1, mid + 1, r, L, R, c);
}
int query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x].sum;
push_down(x);
int mid = (l + r) >> 1, ans = 0;
if(L <= mid)ans += query(x << 1, l, mid, L, R);
if(R > mid)ans += query(x << 1 | 1, mid + 1, r, L, R);
return ans;
}
char get(int x, int l, int r, int pos){
if(l == r)return t[x].ch;
push_down(x);
int mid = (l + r) >> 1;
if(pos <= mid)return get(x << 1, l, mid, pos);
else return get(x << 1 | 1, mid + 1, r, pos);
}
}T;
void manacher(int l, int r){
m = 0; c[0] = '?'; c[++m] = '#';
for(int i = l; i <= r; ++i)c[++m] = T.get(1, 1, n, i), c[++m] = '#';
c[++m] = '&';
for(int i = 1; i <= m; ++i)cnt[i] = 0, p[i] = 0;
int mr = 0, mid = 0;
for(int i = 2; i < m; ++i){
if(i < mr)p[i] = min(p[mid * 2 - i], mr - i);
else p[i] = 1;
while(c[i - p[i]] == c[i + p[i]])++p[i];
if(i + p[i] > mr)mr = i + p[i], mid = i;
++cnt[i - min(k, p[i]) + 1]; --cnt[i + 1];
}
for(int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1], p[i] = 0;
// for(int i = 1; i <= m; ++i)printf("%d ", cnt[i]); printf("\n");
}
char ch[3];
void modify(int l, int r, char c){
if(l <= r - k + 1)T.modify(1, 1, n, l, r - k + 1, k);
T.update(1, 1, n, l, r, c);
manacher(max(1, l - k + 1), min(n, l + k));
for(int i = 2, pos = max(1, l - k + 1); i < m && pos <= l; i += 2, ++pos)
T.modify(1, 1, n, pos, pos, min(k, cnt[i]));
manacher(max(1, r - k + 2), min(n, r + k));
for(int i = 2, pos = max(1, r - k + 2); pos <= r && i < m; i += 2, ++pos)
T.modify(1, 1, n, pos, pos, min(k, cnt[i]));
}
ll query(int l, int r){
ll ans = 0;
if(l <= r - k)ans += T.query(1, 1, n, l, r - k);
manacher(max(l, r - k + 1), r);
for(int i = 2; i < m; i += 2)ans += cnt[i];
return ans;
}
int main(){
scanf("%s", s + 1);
scanf("%d%d",&k,&q);
n = strlen(s + 1);
T.built(1, 1, n);
manacher(1, n);
for(int i = 1; i <= n; ++i)T.modify(1, 1, n, i, i, cnt[i << 1]);
for(int i = 1, op, l, r; i <= q; ++i){
scanf("%d%d%d",&op, &l, &r);
if(op & 1){
scanf("%s",ch + 1);
modify(l, r, ch[1]);
}else printf("%lld\n",query(l, r));
}
return 0;
}
B. recollection
是两个点 树上的 深度
是两个点 树上的 长度
令深度最深,则只需考虑 序相邻的两个点
那么在一个树上维护另外一个树的点的 序集,每次启发式合并即可
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 = 800005;
struct node{
int len, fa;
map<int, int>ch;
}d[maxn];
int up[maxn], pos[maxn];
int n;
queue<int>q;
vector<int>t[maxn], g[maxn];
int son[maxn], dep[maxn], fa[maxn], si[maxn], top[maxn], id[maxn], dfn[maxn], tim;
int tf[maxn], td[maxn];
void dfs1(int x){
si[x] = 1;
for(int v : g[x]){
dep[v] = dep[x] + 1; fa[v] = x;
dfs1(v);
si[x] += si[v];
son[x] = si[son[x]] > si[v] ? son[x] : v;
}
}
void dfs2(int x, int tp){
top[x] = tp; dfn[x] = ++tim; id[tim] = x;
if(son[x])dfs2(son[x], tp);
for(int v : g[x])if(v != son[x]){
dfs2(v, v);
}
}
int lca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]])swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
struct SAM{
int cnt = 1;
int ins(int las, int c){
int fa = las, now = las = ++cnt;
d[now].len = d[fa].len + 1;
for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
if(!fa)d[now].fa = 1;
else{
int x = d[fa].ch[c];
if(d[x].len == d[fa].len + 1)d[now].fa = x;
else{
int clone = ++cnt; d[clone] = d[x];
d[clone].len = d[fa].len + 1;
d[now].fa = d[x].fa = clone;
for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
}
}
return now;
}
void built(){for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);}
}S;
int ans;
set<int>s[maxn];
int merge(set<int>&x, set<int>&y){
if(x.size() < y.size())swap(x, y);
int res = 0;
for(int v : y){
auto it = x.lower_bound(v);
if(it != x.end())res = max(res, d[lca(id[v], id[*it])].len);
if(it != x.begin())res = max(res, d[lca(id[v], id[*--it])].len);
}
for(int v : y)x.insert(v); y.clear();
return res;
}
void solve(int x){
s[x].insert(dfn[pos[x]]);
for(int v : t[x]){
solve(v);
ans = max(ans, merge(s[x], s[v]) + td[x]);
}
}
int main(){
n = read();
for(int i = 2; i <= n; ++i){
tf[i] = read(), up[i] = read();
t[tf[i]].push_back(i);
}
q.push(1); pos[1] = 1;
while(!q.empty()){
int x = q.front(); q.pop();
for(int v : t[x]){
pos[v] = S.ins(pos[x], up[v]);
q.push(v); td[v] = td[x] + 1;
}
}
S.built();dfs1(1); dfs2(1, 1);
solve(1);
printf("%d\n",ans);
return 0;
}
C. 回忆树
分三部分处理,跨越 的一小段暴力
另外两段考虑写成差分的形式,然后离线下来放到 自动机上做
对询问串建立 自动机,然后 原树跑匹配,匹配到了询问串就加一
用树状数组维护子树和即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 2e5 + 55;
int n, m, tot, head[maxn];
struct edge{
int to, net; char val;
}e[maxn << 1 | 1];
char s[maxn], up[maxn];
int len;
void add(int u, int v, char c){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
e[tot].val = c;
}
struct Q{int id, pos, opt;};
vector<Q>que[maxn];
int ans[maxn];
int dfn[maxn], si[maxn], tim;
struct AC{
vector<int>g[maxn];
int ch[maxn][26], fail[maxn], cnt = 1;
int ins(int id){
int now = 1;
for(int i = 1; i <= len; ++i){
if(!ch[now][s[i] - 'a'])ch[now][s[i] - 'a'] = ++cnt;
now = ch[now][s[i] - 'a'];
}
return now;
}
queue<int>q;
void built(){
for(int i = 0; i < 26; ++i)if(ch[1][i])q.push(ch[1][i]), fail[ch[1][i]] = 1; else ch[1][i] = 1;
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = 0; i < 26; ++i)
if(ch[x][i])fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
else ch[x][i] = ch[fail[x]][i];
}
for(int i = 2; i <= cnt; ++i)g[fail[i]].push_back(i);
}
void dfs(int x){
dfn[x] = ++tim;
si[x] = 1;
for(int v : g[x])dfs(v), si[x] += si[v];
}
}A;
int fa[maxn][21], dep[maxn];
void dfs(int x){
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa[x][0])continue;
fa[v][0] = x; up[v] = e[i].val; dep[v] = dep[x] + 1;
dfs(v);
}
}
int lca(int u, int v){
if(dep[u] < dep[v])swap(u, v);
for(int i = 20; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))u = fa[u][i];
if(u == v)return u;
for(int i = 20; i >= 0; --i)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
int kfa(int x, int k){
if(k <= 0)return x;
for(int i = 20; i >= 0; --i)if(k & (1 << i))x = fa[x][i];
return x;
}
char c[maxn], st[maxn];
int nxt[maxn], lenc;
void get_c(int u, int v){
int la = 0, lb = 0;
while(u != v){
if(dep[u] > dep[v]){
c[++la] = up[u];
u = fa[u][0];
}else{
st[++lb] = up[v];
v = fa[v][0];
}
}
for(int i = lb; i >= 1; --i)c[++la] = st[i];
lenc = la;
}
void kmp(){
for(int i = 2, j = 0; i <= len; ++i){
while(j &&s[i] !=s[j + 1])j = nxt[j];
if(s[i] ==s[j + 1]) ++j;
nxt[i] = j;
}
}
int match(){
int res = 0;
for(int now = 0, i = 1; i <= lenc; ++i){
while(now && s[now + 1] != c[i])now = nxt[now];
if(s[now + 1] == c[i]) ++now;
if(now == len)++res, now = nxt[now];
}
return res;
}
struct BIT{
int t[maxn];
int lowbit(int x){return x & -x;}
void modify(int x, int val){while(x <= tim){t[x] += val; x += lowbit(x);}}
int query(int x){int res = 0; while(x){res += t[x]; x -= lowbit(x);}return res;}
int calc(int x){return query(dfn[x] + si[x] - 1) - query(dfn[x] - 1);}
}T;
void sol(int x, int y){
T.modify(dfn[y], 1);
for(Q q : que[x])ans[q.id] += q.opt * T.calc(q.pos);
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v != fa[x][0])sol(v, A.ch[y][up[v] - 'a']);
}
T.modify(dfn[y], -1);
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1, u, v; i < n; ++i){
scanf("%d%d%s",&u,&v,s + 1);
add(u, v, s[1]); add(v, u, s[1]);
}
dfs(1);
for(int j = 1; j <= 20; ++j)
for(int i = 1; i <= n; ++i)fa[i][j] = fa[fa[i][j - 1]][j - 1];
for(int i = 1, u, v; i <= m; ++i){
scanf("%d%d%s",&u,&v,s + 1); len = strlen(s + 1);
int posb = A.ins(i); reverse(s + 1, s + len + 1);
int posa = A.ins(i); reverse(s + 1, s + len + 1);
int lc = lca(u, v), fu = kfa(u, dep[u] - dep[lc] - len + 1), fv = kfa(v, dep[v] - dep[lc] - len + 1);
if(dep[u] - dep[lc] >= len){
que[fu].push_back({i, posa, -1});
que[u].push_back({i, posa, 1});
}
if(dep[v] - dep[lc] >= len){
que[fv].push_back({i, posb, -1});
que[v].push_back({i, posb, 1});
}
get_c(fu, fv); kmp(); ans[i] = match();
}
A.built(); A.dfs(1);
sol(1, 1);
for(int i = 1; i <= m; ++i)printf("%d\n",ans[i]);
return 0;
}
数学1
A. young
表示 个点,取值范围 最小生成树边权和
考虑当前位为 可以划分两个集合 那么他们一定内部连边,然后用一条边连起来
考虑计算这条边的贡献
如果该位都相同
否则枚举一个集合大小
其中 方案数乘贡献
表示集合大小为 的两点集之间连一条边的价值和,点权在 之间
之间求不好做,考虑求最小边权大于等于某数的方案数
表示集合 点权 , 最小边权大于等于 的方案数
考虑把 按照该位为 划分成 然后考虑这位的贡献
当 或者一个点集空了,那么剩下的点权就没有限制了,边界为
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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 = 258280327;
const int maxn = 259;
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;
}
int c[55][55], p2[maxn * maxn];
int rf[55][maxn];
int rg[55][55][maxn];
int rp[55][55][maxn][maxn];
int p(int s, int t, int m, int k){
if(s > t)swap(s, t);
if(s == 0 || k <= 0)return p2[(s + t) * m];
if(k >= (1 << m))return 0;
if(rp[s][t][m][k])return rp[s][t][m][k];
int ans = 0;
if((k & (1 << (m - 1))))
ans = 2ll * p(s, t, m - 1, k ^ (1 << (m - 1))) % mod;
else{
for(int s0 = 0; s0 <= s; ++s0){
for(int t0 = 0; t0 <= t; ++t0){
int s1 = s - s0, t1 = t - t0;
if((s1 == 0 && t0 == 0) || (s0 == 0 && t1 == 0)) ans = (ans + p(s, t, m - 1, k - (1 << (m - 1)))) % mod;
else ans = (ans + 1ll * c[s][s0] * c[t][t0] % mod * p(s0, t0, m - 1, k) % mod * p(s1, t1, m - 1, k)) % mod;
}
}
}
return rp[s][t][m][k] = ans;
}
int g(int s, int t, int m){
if(m == 0)return 0;
if(s > t)swap(s, t);
if(rg[s][t][m])return rg[s][t][m];
int ans = 0;
for(int k = 1; k < (1 << m); ++k)ans = (ans + p(s, t, m, k)) % mod;
return rg[s][t][m] = ans;
}
int f(int n, int m){
if(m == 0 || n == 1)return 0;
if(rf[n][m])return rf[n][m];
int ans = 2 * f(n, m - 1) % mod;
for(int i = 1; i < n; ++i){
int res = (1ll * p2[(n - i) * (m - 1)] * f(i, m - 1) % mod + 1ll * p2[i * (m - 1)] * f(n - i, m - 1) % mod) % mod;
res = (0ll + res + g(i, n - i, m - 1) + p2[(n + 1) * (m - 1)]) % mod;
ans = (ans + 1ll * c[n][i] * res) % mod;
}
return rf[n][m] = ans;
}
int n, m;
int main(){
freopen("young.in","r",stdin);
freopen("young.out","w",stdout);
n = read(), m = read();
p2[0] = 1;
for(int i = 1; i <= 1005; ++i)p2[i] = (p2[i - 1] + p2[i - 1]) % mod;
for(int i = 0; i <= n; ++i){
c[i][0] = 1;
for(int j = 1; j <= i; ++j)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
}
printf("%lld\n",1ll * f(n, m) * qpow(qpow(2, n * m), mod - 2) % mod);
return 0;
}
B. Simple
想错了
首先,有循环节的一定不满足,没有循环节的找到最小表示法方案唯一
于是枚举循环节长度得到
答案为
后面那个等比乘等差就套路搞搞就行。。。。
前面那个杜教筛
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 258280327, mx = 1000000, maxn = mx + 5;
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;
}
int mu[maxn], prime[maxn], cnt, f[maxn];
bool flag[maxn];
void init(){
mu[1] = 1;
for(int i = 2; i <= mx; ++i){
if(!flag[i])prime[++cnt] = i, mu[i] = -1;
for(int j = 1; j <= cnt && i * prime[j] <= mx; ++j){
flag[i * prime[j]] = true;
if(i % prime[j] == 0)break;
mu[i * prime[j]] = -mu[i];
}
}
for(int i = 1; i <= mx; ++i)f[i] = (f[i - 1] + i * mu[i]) % mod;
}
ll n;
unordered_map<int, int>mp;
ll calc(ll l, ll r){return 1ll * (l + r) % mod * (r - l + 1) % mod * ((mod + 1) >> 1) % mod;}
int F(ll n){
if(n <= mx)return f[n];
if(mp.count(n))return mp[n];
int ans = 1;
for(ll l = 2, r; l <= n; l = r + 1){
r = n / (n / l);
ans = (ans - 1ll * calc(l, r) * F(n / l)) % mod;
}
return mp[n] = (ans + mod) % mod;
}
int G(ll n){
int tp = qpow(10, (n + 1) % (mod - 1));
int res = 1ll * (tp - 10) * qpow(9, mod - 2) % mod;
res = (1ll * n * tp - res) % mod;
res = 1ll * res * qpow(9, mod - 2) % mod;
return res;
}
int main(){
// freopen("simple.in","r",stdin);
// freopen("simple.out","w",stdout);
cin >> n; init();
int ans = 0;
for(ll l = 1, r; l <= n; l = r + 1){
r = n / (n / l);
ans = (ans + 1ll * (F(r) - F(l - 1)) * G(n / l)) % mod;
}
ans = (ans % mod + mod) % mod;
printf("%d\n",ans);
return 0;
}
C. mate
之前考过式子相同的,好像还有个 式子
我写的 ,复杂度不太对劲,会 飞,好在数据水
之前做过那个写法挺好,就是把数写成 的质因子的幂次和非 的质因子的部分相乘的形式
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1000005;
ll qpow(ll x, ll y, ll mod){
ll ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
ll exgcd(ll a, ll b, ll &x, ll &y){
if(b == 0){x = 1; y = 0; return a;}
ll gcd = exgcd(b, a % b, y, x);
y -= a / b * x; return gcd;
}
ll inv(ll a, ll b){
ll x, y; exgcd(a, b, x, y);
return (x % b + b) % b;
}
typedef pair<int, int> pii;
map<pii, int>mp;
ll get_f(ll n, ll p, ll pk){
if(n == 0)return 1;
if(mp[pii(n, p)])return mp[pii(n, p)];
ll res = 1, tmp = n % pk;
for(ll i = 2; i <= tmp; ++i)if(i % p)res = 1ll * res * i % pk;
ll num = res;
if(n / pk)for(ll i = tmp + 1; i < pk; ++i)if(i % p)res = 1ll * res * i % pk;
return mp[pii(n, p)] = 1ll * get_f(n / p, p, pk) * qpow(res, n / pk, pk) % pk * num % pk;
}
ll get_c(ll n, ll m, ll p, ll pk){
ll res = 0, d = n - m;
for(ll i = p; i <= n; i *= p)res += n / i;
for(ll i = p; i <= m; i *= p)res -= m / i;
for(ll i = p; i <= d; i *= p)res -= d / i;
return 1ll * qpow(p, res, pk) * get_f(n, p, pk) % pk * inv(1ll * get_f(m, p, pk) * get_f(d, p, pk) % pk, pk) % pk;
}
int mod, p[105], pk[105], cnt, fac[105][maxn], ifac[105][maxn];
int c(int n, int m, int p, int i){return 1ll * fac[i][n] * ifac[i][n - m] % p * ifac[i][m] % p;}
int lucas(int n, int m, int p, int i){
if(m > n)return 0;
if(m == n || n == 0 || n == 1)return 1;
if(n < p && m < p)return c(n, m, p, i);
return 1ll * lucas(n % p, m % p, p, i) * lucas(n / p, m / p, p, i) % p;
}
void init(){
int x = mod;
for(ll i = 2; i * i <= x; ++i){
if(x % i)continue;
int tmp = 1;
while(x % i == 0)x /= i, tmp *= i;
++cnt; p[cnt] = i; pk[cnt] = tmp;
}
if(x != 1){++cnt; p[cnt] = pk[cnt] = x;}
for(int k = 1; k <= cnt; ++k)if(p[k] == pk[k]){
fac[k][0] = ifac[k][0] = 1;
int up = min(p[k], 1000001);
for(int i = 1; i < up; ++i)fac[k][i] = 1ll * fac[k][i - 1] * i % p[k];
ifac[k][up - 1] = qpow(fac[k][up - 1], p[k] - 2, p[k]) % p[k];
for(int i = up - 2; i >= 1; --i)ifac[k][i] = 1ll * ifac[k][i + 1] * (i + 1) % p[k];
}
}
ll exlucas(ll n, ll m){
if(n == m)return 1;
ll ans = 0;
for(int i = 1; i <= cnt; ++i){
ll c = mod / pk[i], getc = 0;
if(p[i] == pk[i])getc = lucas(n, m, p[i], i);
else getc = get_c(n, m, p[i], pk[i]);
ans = (ans + 1ll * getc * c % mod * inv(c, pk[i]) % mod) % mod;
}
return ans;
}
int n, x, y;
int main(){
freopen("mate.in","r",stdin);
freopen("mate.out","w",stdout);
cin >> n >> mod >> x >> y; init();
if(x < 0) x = -x; if(y < 0)y = -y;
int res = n - x - y;
if(res & 1)printf("0\n");
else{
int up = res / 2;
ll ans = 0;
for(int i = 0; i <= up; ++i){
ans = (ans + 1ll * exlucas(n, x + i + i) * exlucas(x + i + i, i) % mod * exlucas(y + (up - i) * 2, up - i) % mod) % mod;
}
printf("%lld\n",ans);
}
return 0;
}
数学2
A. 猜拳游戏
如果得到胜负概率,那么高斯消元即可得到答案
但是有个平局非常恶心,简单思考发现只需要重新分配概率即可
即令 表示考虑平局时两人胜利的概率
把胜率改成 即可
然后不会了
学个套路
求最大值
求 最小值
形如分数规划,考虑二分答案
于是就可以 了,不要写记搜,常数太大
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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 = 2005, base = 1002;
int n, m1, m2;
double p[maxn][3], winp;
double f[maxn][maxn + maxn], a[maxn + maxn][maxn + maxn];
double mid;
bool check(){
for(int i = base + 1; i <= base + n; ++i)f[n + 1][i] = mid;
for(int i = base - 1; i >= base - n; --i)f[n + 1][i] = -1;
f[n + 1][base] = 0;
for(int round = n; round >= 1; --round){
for(int wind = base - round + 1; wind <= base + round - 1; ++wind){
double p1 = p[round][0] * f[round + 1][wind] + p[round][1] * f[round + 1][wind - 1] + p[round][2] * f[round + 1][wind + 1];
double p2 = p[round][0] * f[round + 1][wind + 1] + p[round][1] * f[round + 1][wind] + p[round][2] * f[round + 1][wind - 1];
double p3 = p[round][0] * f[round + 1][wind - 1] + p[round][1] * f[round + 1][wind + 1] + p[round][2] * f[round + 1][wind];
f[round][wind] = max({p1, p2, p3});
}
}
return f[1][base] > 1e-8;
}
void gauss(int n){
for(int i = 0; i <= n; ++i){
int now = i;
for(int j = i + 1; j <= n; ++j)if(abs(a[j][i]) > abs(a[now][i]))now = j;
for(int j = i; j <= n + 1; ++j)swap(a[now][j], a[i][j]);
for(int j = n + 1; j >= i; --j)a[i][j] /= a[i][i];
for(int j = 0; j <= n; ++j)if(i != j)
for(int k = n + 1; k >= i; --k)
a[j][k] -= a[j][i] * a[i][k];
}
}
int main(){
n = read(), m1 = read(), m2 = read();
while(n || m1 || m2){
for(int i = 1; i <= n; ++i){
p[i][0] = read() / 100.0;
p[i][1] = read() / 100.0;
p[i][2] = read() / 100.0;
}
double l = 0, r = 1e6;
while(r - l > 1e-6){
mid = (l + r) / 2;
if(check() > 0)r = mid;
else l = mid;
}
winp = (l + r) / 2;
winp = 1 / (1 + winp);
for(int i = 0; i <= m1 + m2 + 1; ++i)
for(int j = 0; j <= m1 + m2 + 1; ++j)
a[i][j] = (i == j);
for(int i = 1; i < m1 + m2; ++i)a[i][i + 1] = -winp, a[i][i - 1] = winp - 1;
a[m1 + m2][m1 + m2 + 1] = 1;
gauss(m1 + m2);
printf("%.5lf\n",a[m2][m1 + m2 + 1]);
n = read(), m1 = read(), m2 = read();
}
return 0;
}
B. B君的回忆
结论题吧大概,就是猜测打表发现有循环节
然后 求解
优化在于一个结论,设 表示模 意义下循环节长度
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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;
}
int mod;
struct matrix{
ll a[2][2];
matrix(){a[0][0] = a[1][0] = a[0][1] = a[1][1] = 0;}
void I(){a[0][0] = a[1][1] = 1; a[1][0] = a[0][1] = 0;}
static matrix II(){ matrix x; x.I(); return x;}
friend matrix operator * (const matrix &x, const matrix &y){
matrix ans;
ans.a[0][0] = ( x.a[0][0] * y.a[0][0] + x.a[0][1] * y.a[1][0]) % mod;
ans.a[0][1] = ( x.a[0][0] * y.a[0][1] + x.a[0][1] * y.a[1][1]) % mod;
ans.a[1][0] = ( x.a[1][0] * y.a[0][0] + x.a[1][1] * y.a[1][0]) % mod;
ans.a[1][1] = ( x.a[1][0] * y.a[0][1] + x.a[1][1] * y.a[1][1]) % mod;
return ans;
}
void qpow(int k){
matrix ans; ans.I();
for(; k; k >>= 1, (*this) = (*this) * (*this))if(k & 1)ans = ans * (*this);
(*this) = ans;
}
friend bool operator ==(const matrix& x, const matrix& y){
return memcmp(&x, &y, sizeof(x)) == 0;
}
}trans, f1;
struct hash_mat{
size_t operator ()(const matrix& x)const{
static const int B = 97;
size_t val = 0;
for(int i = 0; i < 2; ++i){
for(int j = 0; j < 2; ++j){
val = val * B + x.a[i][j];
}
}
return val;
}
};
int a, b, n, k, p;
int g(int n, int p){
if(n == 0)return a;
if(n == 1)return b;
mod = p; matrix now = f1;
matrix tr = trans; tr.qpow(n - 1);
now = now * tr;
return (now.a[0][0] % p + p) % p;
}
unordered_map<matrix, int, hash_mat>mp;
unordered_map<int, int>rb;
int bsgs(int p){
if(rb.count(p))return rb[p];
int sq = 2 * ceil(sqrt(p));
mp.clear(); mod = p;
matrix x, tr; x.I(); mp[x] = 0;
tr = trans; tr.a[1][0] = p - 1;
for(int i = 1; i < sq; ++i){
x = x * tr; mp[x] = i;
}
x = x * tr;
matrix y = x;
for(int i = 1; i <= sq; ++i){
if(mp.count(y))return rb[p] = i * sq - mp[y];
y = y * x;
}
abort();
}
unordered_map<int, int>rp;
int prime[1005], cprime[1005], cnt;
int getmod(int p){
if(rp.count(p))return rp[p];
int x = p; cnt = 0;
for(int i = 2; i * i <= x; ++i)if(x % i == 0){
prime[++cnt] = i; cprime[cnt] = 0;
while(x % i == 0)x /= i, ++cprime[cnt];
}
if(x > 1)prime[++cnt] = x,cprime[cnt] = 1;
int ans = 1;
for(int i = 1; i <= cnt; ++i){
int len = bsgs(prime[i]);
for(int j = 1; j < cprime[i]; ++j)len *= prime[i];
ans = ans / __gcd(ans, len) * len;
}
return rp[p] = ans;
}
int f(int n, int k, int p){
if(k == 0)return n;
return g(f(n, k - 1, getmod(p)), p);
}
int main(){
trans.a[0][0] = 3; trans.a[0][1] = 1; trans.a[1][0] = -1;
int t = read();
for(int i = 1; i <= t; ++i){
a = read(), b = read(), n = read(), k = read(), p = read();
f1.a[0][0] = b; f1.a[0][1] = a;
printf("%d\n",f(n, k, p));
}
return 0;
}
C. 小 H 爱染色
神仙组合意义题
求
表示 个球染色,并且第一个黑色的方案数
把 展开
然后开始胡扯
考虑后面的实际含义,对于 就是上面的解释, 为 个球,每次染色一个
我们枚举了一个 , 而分开两部分考虑两种染色
那么我们可以认为前 个为第二种染色,剩下的为第一种染色
那么 其实可以转化掉
如果枚举前后两部分染黑 个
那么第 个位置就是 , 但是在哪里并不重要了
考虑算出此时的方案
选出这 个位置
第一种染色,考虑先选出 个染色, 个没染的下一次必定染, 个则需要在之前染过的 个选择
第二种染色,二项式反演转化成至多
整个式子变成
把 放到后面,发现恰好成了
先卷后面
然后卷上
最后乘上
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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 = 5000005;
const int mod = 998244353;
int fac[maxn], ifac[maxn];
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;
}
int deg, wn[maxn], rev[maxn];
void init(int len){
deg = 1; while(deg < len)deg <<= 1;
wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
for(int i = 1; i < deg; ++i){
wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
rev[i] = rev[i >> 1] >> 1;
if(i & 1)rev[i] |= (deg >> 1);
}
}
struct poly{
int f[maxn];
int &operator [](const int &i){return f[i];}
void ntt(){
for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
for(int i = 0; i < deg; i += l){
int x, y;
for(int j = i; j < i + hl; ++j){
x = f[j], y = 1ll * wn[deg / l * (j - i)] * f[j + hl] % mod;
f[j] = (x + y) % mod; f[j + hl] = (x - y + mod) % mod;
}
}
}
void operator *= (poly &g){
ntt(); g.ntt();
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
intt();
}
void intt(){
ntt(); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
}
}f, g;
void pre(int mx){
fac[0] = ifac[0] = 1; for(int i = 1; i <= mx; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[mx] = qpow(fac[mx], mod - 2); for(int i = mx - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
}
int c(int n, int m){return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int n, m;
int main(){
n = read(), m = read(); pre(3 * m);
init(m + m + 5);
for(int i = 0; i <= m; ++i)f[i] = 1ll * read() * ifac[i] % mod;
for(int i = 0; i <= m; ++i)g[i] = (i & 1) ? mod - ifac[i] : ifac[i];
f *= g; for(int i = 0; i <= m; ++i)f[i] = 1ll * f[i] * fac[i] % mod;
for(int i = m + 1; i < deg; ++i)f[i] = 0;
for(int i = 0; i < deg; ++i)g[i] = 0;
for(int i = m; i <= m + m; ++i)g[i - m] = 1ll * c(i, m) * c(m, m + m - i) % mod;
f *= g;
int binom = ifac[m];
for(int i = 0; i < m; ++i)binom = 1ll * binom * (n - i) % mod;
int ans = 0;
for(int i = m; i <= m * 3; i++){
ans = (ans + 1ll * binom * f[i - m]) % mod;
binom = 1ll * binom * (n - i) % mod * fac[i] % mod * ifac[i + 1] % mod;
}
printf("%d\n",ans);
return 0;
}
数学3
A. 解方程
没有限制直接插板法
有限制
在总数上减去 ,先给他 个,则转换为插板法
对于 进行容斥,枚举哪些不合法
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1000005;
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;
}
ll qpow(ll x, ll y, ll mod){
ll ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
ll exgcd(ll a, ll b, ll &x, ll &y){
if(b == 0){x = 1; y = 0; return a;}
ll gcd = exgcd(b, a % b, y, x);
y -= a / b * x; return gcd;
}
ll inv(ll a, ll b){
ll x, y; exgcd(a, b, x, y);
return (x % b + b) % b;
}
typedef pair<int, int> pii;
map<pii, int>mp;
ll get_f(ll n, ll p, ll pk){
if(n == 0)return 1;
if(mp[pii(n, p)])return mp[pii(n, p)];
ll res = 1, tmp = n % pk;
for(ll i = 2; i <= tmp; ++i)if(i % p)res = 1ll * res * i % pk;
ll num = res;
if(n / pk)for(ll i = tmp + 1; i < pk; ++i)if(i % p)res = 1ll * res * i % pk;
return mp[pii(n, p)] = 1ll * get_f(n / p, p, pk) * qpow(res, n / pk, pk) % pk * num % pk;
}
ll get_c(ll n, ll m, ll p, ll pk){
ll res = 0, d = n - m;
for(ll i = p; i <= n; i *= p)res += n / i;
for(ll i = p; i <= m; i *= p)res -= m / i;
for(ll i = p; i <= d; i *= p)res -= d / i;
return 1ll * qpow(p, res, pk) * get_f(n, p, pk) % pk * inv(1ll * get_f(m, p, pk) * get_f(d, p, pk) % pk, pk) % pk;
}
int mod, p[105], pk[105], cnt, fac[105][maxn], ifac[105][maxn];
int c(int n, int m, int p, int i){return 1ll * fac[i][n] * ifac[i][n - m] % p * ifac[i][m] % p;}
int lucas(int n, int m, int p, int i){
if(m > n)return 0;
if(m == n || n == 0 || n == 1)return 1;
if(n < p && m < p)return c(n, m, p, i);
return 1ll * lucas(n % p, m % p, p, i) * lucas(n / p, m / p, p, i) % p;
}
void init(){
int x = mod;
for(ll i = 2; i * i <= x; ++i){
if(x % i)continue;
int tmp = 1;
while(x % i == 0)x /= i, tmp *= i;
++cnt; p[cnt] = i; pk[cnt] = tmp;
}
if(x != 1){++cnt; p[cnt] = pk[cnt] = x;}
for(int k = 1; k <= cnt; ++k)if(p[k] == pk[k]){
fac[k][0] = ifac[k][0] = 1;
int up = min(p[k], 1000001);
for(int i = 1; i < up; ++i)fac[k][i] = 1ll * fac[k][i - 1] * i % p[k];
ifac[k][up - 1] = qpow(fac[k][up - 1], p[k] - 2, p[k]) % p[k];
for(int i = up - 2; i >= 1; --i)ifac[k][i] = 1ll * ifac[k][i + 1] * (i + 1) % p[k];
}
}
ll exlucas(ll n, ll m){
if(n == m)return 1;
ll ans = 0;
for(int i = 1; i <= cnt; ++i){
ll c = mod / pk[i], getc = 0;
if(p[i] == pk[i])getc = lucas(n, m, p[i], i);
else getc = get_c(n, m, p[i], pk[i]);
ans = (ans + 1ll * getc * c % mod * inv(c, pk[i]) % mod) % mod;
}
return ans;
}
int n, x, y, m, lim[maxn];
signed main(){
int t = read(); mod = read();
init();
for(int r = 1; r <= t; ++r){
n = read(), x = read(), y = read(), m = read();
for(int i = 1; i <= x; ++i)lim[i] = read();
for(int i = 1; i <= y; ++i)m -= read() - 1;
int ans = 0;
for(int i = 0; i < (1 << x); ++i){
int mm = m, opt = 1;
for(int j = 1; j <= x; ++j)if((1 << (j - 1)) & i)opt *= -1, mm -= lim[j];
if(mm < n)continue;
ans = (ans + opt * exlucas(mm - 1, n - 1));
}
ans = (ans % mod + mod) % mod;
printf("%d\n",ans);
}
return 0;
}
B. 宇宙序列
暴力的话直接做 是
发现 之后,每个位置最终答案只与现在该位置的数有关,而且值域较小
于是考虑对每个数求出答案,最后统一 回去
使用倍增,答案
code
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
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;
}
int qpow(int x, int y, int mod){
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 = 600005;
const int mod = 10007;
void fwt(int f[], int deg, int opt){
for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
for(int i = 0; i < deg; i += l)
for(int j = i; j < i + hl; ++j){
int x = f[j], y = f[j + hl];
f[j] = 1ll * opt * (x + y) % mod;
f[j + hl] = 1ll * opt * (x - y + mod) % mod;
}
}
int n, p, k, f[maxn], g[maxn];
int dp[33][maxn];
int main(){
n = read(), p = read(), k = read();
for(int i = 0; i < (1 << n); ++i)f[i] = read();
fwt(f, 1 << n, 1);
for(int i = 0; i < mod; ++i)dp[0][i] = i;
for(int i = 1; i < 30; ++i)
for(int j = 0; j < mod; ++j)
dp[i][j] = (dp[i - 1][j] + dp[i - 1][qpow(j, qpow(2, 1 << (i - 1), mod - 1), mod)]) % mod;
for(int i = 0; i < mod; ++i){
int x = i;
for(int j = 29; j >= 0; --j)if((p + 1) & (1 << j)){
g[i] = (g[i] + dp[j][x]) % mod;
x = qpow(x, qpow(2, 1 << j, mod - 1), mod);
}
}
for(int i = 0; i < (1 << n); ++i)f[i] = g[f[i]];
fwt(f, 1 << n, (mod + 1) >> 1);
printf("%d\n",f[k]);
return 0;
}
C. exp
https://www.cnblogs.com/skyh/p/12134383.html
code
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 405;
double c[maxn][maxn], pw[maxn][maxn];
char s[maxn];
int n, cnt[maxn][maxn];
bool rf[maxn][maxn], rp[maxn][maxn];
double p[maxn][maxn];
double P(int l, int r);
double G(int l, int r, int k){
int a = cnt[l][k], b = cnt[k + 1][r];
if(s[r] == 'X' || s[k] == 'X')return 0;
return pw[k - l + 1][a] * pw[r - k][b - 1] / pw[r - l + 1][a + b - 1] * c[a + b - 2][a - 1] * P(l, k) * P(k + 1, r);
}
double P(int l, int r){
if(rp[l][r])return p[l][r]; p[l][r] = 0;
if(s[r] == 'X')return 0;
if(cnt[l][r] == 1)return 1;
rp[l][r] = 1;
for(int i = l; i < r; ++i)p[l][r] += G(l, r, i);
// printf("%d %d %lf\n",l, r, p[l][r]);
return p[l][r];
}
double f[maxn][maxn];
double F(int l, int r){
if(rf[l][r])return f[l][r];
f[l][r] = 0; rf[l][r] = 1;
if(s[r] == 'X' || P(l, r) < 1e-12 || cnt[l][r] == 1)return 0;
for(int i = l; i < r; ++i)f[l][r] += G(l, r, i) * (F(l, i) + F(i + 1, r) + (i - l) / 2.0);
f[l][r] /= P(l, r);
// printf("%d %d %lf\n",l, r, f[l][r]);
return f[l][r];
}
void solve(){
scanf("%s",s + 1);
n = strlen(s + 1);
for(int i = 1; i <= n; ++i)s[i + n] = s[i];
for(int i = 1; i <= n + n; ++i)cnt[i][i] = s[i] == '.';
for(int i = 1; i <= n + n; ++i)
for(int j = i + 1; j <= n + n; ++j)
cnt[i][j] = cnt[i][j - 1] + (s[j] == '.');
if(cnt[1][n] == 0){printf("0\n"); return;}
memset(rf, 0, sizeof(rf));
memset(rp, 0, sizeof(rp));
double ans = (n - 1) / 2.0;
for(int i = 1; i <= n; ++i)ans += F(i, i + n - 1) * P(i, i + n - 1);
printf("%.8lf\n",ans);
}
int main(){
for(int i = 0; i <= 400; ++i){
c[i][0] = pw[i][0] = 1;
for(int j = 1; j <= i; ++j)
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
for(int j = 1; j <= 400; ++j)
pw[i][j] = pw[i][j - 1] * i;
}
int t; scanf("%d",&t);
for(int r = 1; r <= t; ++r)solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下