202? 做题记录

也快退役了,早日飞升。

Codechef December Challenge 2020

-CALCULUS

题目描述:给定正整数 \(n\),求

\[\int_0^{+\infty}\frac{e^{2\pi x}-1}{e^{2\pi x}+1}(\frac1x-\frac x{n^2+x^2})\bmod 998244353 \]

数据范围:\(n\le 10^6\)

solution

帕塞瓦尔定理给出了:

\[\int_0^{+\infty}f(x)g(x)\text dx=\frac 2\pi\int_0^{+\infty}F_c(\omega)G_c(\omega)\text d\omega \]

其中 \(F_c(\omega)=\int_0^{+\infty}f(x)\sin(\omega x)\text dx\)\(f(x)\) 的正弦傅里叶变换。

LCASQRT

题目描述:给定正整数 \(n,p\),设 \(\mathbb A_p\) 是所有元素 \(<p\) 且长度为 \(n\) 的自然数序列。给定 \(n\) 个点的有根树和序列 \(c\in\mathbb A_p\),定义 LCA 卷积 \(c=a\times b\)

\[c_{\text{LCA(u,v)}}\leftarrow^+ a_ub_v(\text{mod} \ p) \]

求使得 \(a\in\mathbb A_p,c=a\times a\)\(a\) 数量\(\bmod 998244353\),并给出一个满足要求的序列 \(a\)(若存在)。\(T\) 组数据。

数据范围:\(T\le 10^5,\sum n\le 5\times 10^5,3\le p\le 10^9+7,p\) 是质数。

solution

\(\hat c\)\(c\) 的子树和,则 \(\hat c_u=\hat a_u^2\),计算二次剩余即可。时间复杂度 \(O(n\log p)\)

DIVPERMS

题目描述:给定集合 \(S\subseteq[1,n]\cap\N\),定义长为 \(n\) 的排列 \(p\) 的权值为 \(\sum_{i=2}^n[\frac{p_i}{p_{i-1}}\in S]\)。对所有 \(k\in[0,n)\cap\N\),求长为 \(n\),权值为 \(k\) 的排列数量\(\bmod 998244353\)

数据范围:\(n\le 40\)

solution

发现贡献权值的 \(p_{i-1}\) 满足 \(p_{i-1}\le\lfloor\frac n2\rfloor\)。dp 考虑从小到大插入数,设 \(f_{i,S}\) 表示所有长为 \(i\) 的排列,且 \(\frac{p_i}{p_{i-1}}\in S\) 用到的 \(p_{i-1}\) 组成的集合为 \(S\)。时间复杂度 \(O(n^22^{\lfloor\frac n2\rfloor})\)

#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int n, dp[41][1<<20], ans[40]; char str[44];
void qmo(int &x){x += x >> 31 & mod;}
int main(){
    scanf("%d%s", &n, str+1); dp[0][0] = 1;
    for(int i = 1;i <= n;++ i){
        int lim = 1<<(i-1>>1);
        for(int S = 0;S < lim;++ S){
            int tmp = dp[i-1][S] - mod; qmo(dp[i][S] += tmp);
            for(int j = 1;j < i;++ j)
                if(!(i % j) && str[i/j] == '1') qmo(dp[i][S | (1<<j-1)] += tmp);
                else if(j<=(i-1>>1) && (S>>j-1&1)) qmo(dp[i][S ^ (1<<j-1)] += tmp);
                else qmo(dp[i][S] += tmp);
        }
    } int lim = 1<<(n>>1);
    for(int i = 0;i < lim;++ i) qmo(ans[__builtin_popcount(i)] += dp[n][i] - mod);
    for(int i = 0;i < n;++ i) printf("%d%c", ans[i], " \n"[i==n-1]);
}

MODPARRS

题目描述:给定长为 \(n\) 的自然数序列 \(A\),求满足以下条件的自然数序列 \(X\) 的数量\(\bmod 998244353\)

  • \(\forall i,X_i<239\)
  • \(\forall i\ne j,X_i\ne X_j\)
  • \(239|\sum_{i=1}^nA_iX_i\)

数据范围:\(n\le 20\)

solution

若任意选择 \(X_1,\dots,X_{n-1}\),则第三个条件唯一确定 \(X_n\)。设集合 \(S=\{i|X_i\ne X_n\}\),则可以把 \(S\) 之外的元素求和并看成一个。

\(dp_S\) 表示 \(S\) 之外的数合并为一个,\(S\) 内部的数任意选择系数的方案数。设 \(sum_S=\sum_{i\notin S}X_i\)

\(sum_S\ne 0\),则方案数为总方案-有至少一个的系数与 \(S\) 之外的系数相同。此时不可能多于一个,则 \(dp_S=\frac{239!}{(239-|S|)!}-\sum_{i\in S}dp_{S-\{i\}}\)

\(sum_S=0\),则可以去掉任意一个元素并加进 \(\complement_US\)\(S\) 之外的系数可以任意选择,所以 \(dp_S=(239-|S|)dp_{S-\{i\}}\),其中 \(i\in S\)

答案即为 \(dp_{2^{n-1}-1}\)。时间复杂度 \(O(2^{n-1})\)

#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int n, fac[20], a[20], dp[1<<19], lim;
void qmo(int &x){x += x >> 31 & mod;}
int sum(int S){
    int res = 0;
    for(int i = 0;i < n;++ i)
        if(S >> i & 1) res += a[i];
    return res % 239;
}
int main(){
    scanf("%d", &n); fac[0] = 1;
    for(int i = 0;i < n-1;++ i) fac[i+1] = (239ll - i) * fac[i] % mod;
    for(int i = 0;i < n;++ i) scanf("%d", a + i);
    dp[0] = sum((1<<n)-1) ? 1 : 239; lim = 1 << n-1;
    for(int S = 1;S < lim;++ S)
        if(sum((1<<n)-1-S)){
            dp[S] = fac[__builtin_popcount(S)];
            for(int i = 0;i < n-1;++ i)
                if(S >> i & 1) qmo(dp[S] -= dp[S ^ (1<<i)]);
        } else dp[S] = (239ll - __builtin_popcount(S)) * dp[S ^ (S & -S)] % mod;
    printf("%d\n", dp[lim-1]);
}

DGMATRIX

题目描述:给定 \(n\times n\) 的矩阵 \(A\),求 \((n+1)\times (n+1)\) 的矩阵 \(B\),使得 \(A_{i,j}=B_{i,j}+B_{i+1,j}+B_{i,j+1}+B_{i+1,j+1}\)

数据范围:\(n\le 100\),保证有解。

solution

设第 \(0\) 行是 \(x_0,x_1,\dots,x_n\),第 \(0\) 列是 \(y_0,y_1,\dots,y_m\),枚举 \(x_0=y_0\),则第 \(i\) 行第 \(j\) 列的数为 \(?+(-1)^{i+j}((-1)^ix_i+(-1)^jy_j-x_0)\),其中 \(?\) 可以通过递推计算。直接列不等式用 spfa 做差分约束即可,时间复杂度 \(O(n^4)\)

弦图 (2)

对于无向简单图 \(G=(V,E)\),定义团树 \(T=(\mathcal V,\mathcal E)\) 满足:

  1. \(\mathcal V\)\(G\) 中所有极大团构成的集合。
  2. 对于 \(v\in V\),包含点 \(v\) 的极大团在 \(T\) 中是连通子集。

%yhx

定理:一个无向简单图是弦图当且仅当它存在团树。

构造 (Bernstein-Goodman 算法):

  1. 找到弦图的所有极大团 \(Q_1,Q_2,\dots,Q_k\)
  2. 若两个极大团 \(Q_i,Q_j\) 有交,则连一条权值为 \(|Q_i\cap Q_j|\) 的边,这个图称为团图
  3. 求团图的最大生成树。

时间复杂度 \(O(k^2)\)

*PALINDEQ

题目描述:给定正整数 \(n\) 和长为 \(n\) 的字符串 \(S\)。定义两个长度为 \(n\) 的字符串 \(A,B\),定义 \(A\)\(B\) 等价当且仅当 \(\forall 1\le l\le r\le n,A[l:r]\)是回文串 \(\Leftrightarrow B[l:r]\)是回文串。求满足存在字符 \(c\) 和字符串 \(X\),使得 \(X\)\(S\) 等价,且 \(\forall p\in P,X_p=c\) 的非空集合 \(P\subseteq[1,n]\cap\N\) 的数量\(\bmod 998244353\)\(T\) 组数据。

数据范围:\(T\le 1000,n\le 2000,|\Sigma|=26\)

solution

这题的条件跟校内 OJ 的某道题比较像...

对于这个条件,若 \(S[l+1:r-1]\) 是回文串而 \(S[l:r]\) 不是回文串,则 \(X_l\ne X_r,X_{l+1}=X_{r-1},\dots\)。将要求相等的点合并,要求不相等的点连边,最终就是求一个类似独立集个数的东西。

这个图是弦图,证明如下:考虑所有的这些区间 \([l,r]\)\(l+1,r-1\) 必定属于同一块,分三种情况:

  1. \(S_l=S_{l+1}\),则 \(l,l+1\) 属于同一块,连接两个点。
  2. \(S_r=S_{r-1}\),与 1. 同理。
  3. \(S_l\ne S_{l+1},S_r\ne S_{r-1}\),有三条边 \((l,r),(l,l+1),(r-1,r)\) 形成三元环。

求出这个弦图并构造出团树。对团树做树形 dp,开一个虚点 \(rt\) 连向所有连通块中的任意一个团,并将 \(rt\) 作为根。设 \(C_u\) 表示团 \(u\) 的点集,\(T_u\) 表示团 \(u\) 的儿子,\(g_u\) 表示仅考虑 \(u\) 的子树,\(u\) 中不选点的方案数,\(f_{u,x}\) 表示仅考虑 \(u\) 的子树,\(u\) 中选 \(x\) 的方案数,\(val_x\) 表示 \(2^{siz}-1\),其中 \(siz\) 是点 \(x\) 包含的字符串中位置个数(上面的合并)。

\[\begin{aligned} g_u&=\prod_{v\in T_u}(g_v+\sum_{x\in C_v-C_u}f_{v,x}) \\ f_{u,x}&=\begin{cases}val_x\prod\limits_{v\in T_u\\x\in C_v}\dfrac{f_{v,x}}{val_x}\prod\limits_{v\in T_u\\x\notin C_v}(g_v+\sum\limits_{y\in C_v-C_u}f_{v,y})&(x\in\bigcup\limits_{v\in T_u}C_v) \\ val_xg_u&\text{otherwise}\end{cases} \end{aligned} \]

答案即为 \(g_{rt}\),时间复杂度 \(O(n^2)\)

Codechef November Challenge 2020

-CHEFSSM

题目描述:给定 \(n\) 个齿轮,第 \(i\) 个齿轮上有 \(A_i+1\) 个齿,每一步可以选择一个齿轮,将其顺时针或逆时针旋转一格,求最少步数的期望值\(\bmod 998244353\) 使得至少存在一个齿轮的某个位置是第 \(0\) 个或第 \(A_i\) 个齿。

数据范围:\(n\le 10^5\)

-PANIC

题目描述:给定 \(k\times k\) 的矩阵 \(M\),求

\[\sum_{i=0}^nF_{a+id}M^i\bmod 998244353 \]

其中 \(F\) 是斐波那契数。\(T\) 组数据。

数据范围:\(\sum k\le 100,a,d\le 10^9,n\le 10^{1000}\)

CF1464E No Game No Life

题目描述:给定 \(n\) 个自然数 \(a_i\),初始自然数 \(x=0\),做如下操作:生成 \([0,n]\) 的随机正整数 \(k\),若 \(k=0\) 则结束操作,否则将 \(x:=x\oplus a_k\) 并重复操作。求最终 \(x\ne 0\) 的概率\(\bmod 998244353\)

数据范围:\(n\le 10^5,a_i<512\)

solution

\(c_i\)\(a_i\) 的桶,则所求即为

\[\sum_{k=0}^{+\infty}\frac{c^k}{(n+1)^{k+1}}=\frac{1}{n+1-c} \]

的第 \(0\) 项,此处乘法是异或卷积,(整数-序列)是对序列的 \(0\) 项做运算。使用 FWT 计算,由于 \(\sum c=n\) 所以不会出现除以 \(0\) 的情况。时间复杂度 \(O(n+V\log V)\)

#include<bits/stdc++.h>
#define PB emplace_back
using namespace std;
typedef long long LL;
const int N = 100003, M = 512, mod = 998244353;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
void qmo(int &x){x += x >> 31 & mod;}
void div2(int &x){if(x & 1) x += mod; x >>= 1;}
int n, m, sg[N], cnt[M], vis[M], inv[N<<1], ans; vector<int> E[N];
void init(int m){
    inv[1] = 1;
    for(int i = 2;i <= m;++ i) inv[i] = mod - (LL) mod / i * inv[mod % i] % mod;
}
void dfs(int x){ sg[x] = 0;
    for(int v : E[x]) if(!~sg[v]) dfs(v);
    for(int v : E[x]) vis[sg[v]] = x;
    while(vis[sg[x]] == x) ++ sg[x];
}
int main(){
    read(n); read(m); init(n<<1|1);
    for(int i = 1, u, v;i <= m;++ i){
        read(u); read(v); E[u].PB(v);
    } memset(sg, -1, sizeof sg); cnt[0] = n+1;
    for(int i = 1;i <= n;++ i){if(!~sg[i]) dfs(i); --cnt[sg[i]];}
    for(int mid = 1;mid < M;mid <<= 1)
        for(int i = 0;i < M;i += mid<<1)
            for(int j = 0;j < mid;++ j){
                int y = cnt[mid+i+j];
                qmo(cnt[mid+i+j] = cnt[i+j] - y);
                qmo(cnt[i+j] += y - mod);
            }
    for(int i = 0;i < M;++ i) qmo(ans += inv[cnt[i]] - mod);
    for(int i = 0;i < 9;++ i) div2(ans); qmo(ans = 1 - ans);
    printf("%d\n", ans);
}

CF1464F My Beautiful Madness

题目描述:给定 \(n\) 个点的树,\(m\) 次操作,对于初始为空的路径多重集合 \(P\)

  1. 给定 \(u,v\),在 \(P\) 中加入一条路径 \((u,v)\)
  2. 给定 \(u,v\),在 \(P\) 中删除一条路径 \((u,v)\)
  3. 给定 \(d\),问是否存在点 \(u\),使得 \(u\)\(P\) 中的每一条路径的距离 \(\le d\)

数据范围:\(n,m\le 2\times 10^5\)

solution

对于询问,发现这样的点 \(u\) 若存在,则一定可以是所有路径的 \(\text{lca}\) 中最深的点的 \(d\) 级祖先。证明显然

然后就可以改为询问 \(d,u\),判断是否 \(u\) 到所有路径的距离都 \(\le d\)

\(u\)\(d\) 级祖先为 \(v\),则所有路径一定要与 \(v\) 的子树有交。然后若路径不完全在 \(v\) 的子树内,则一定合法,否则只需判断 \(\text{lca}\)\(u\) 的距离是否 \(\le d\)

那么现在的问题相当于:在点集 \(S\) 中加删点,求 \(S\) 的子树内点到 \(x\) 的距离最大值。这个可以用线段树维护区间直径的方法做,时间复杂度 \(O((n+m)\log n)\)

#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int, int> pii;
const int N = 200003;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, q, cnt, tcnt, ocr[N], head[N], to[N<<1], nxt[N<<1];
void add(int a, int b){to[++cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;}
int siz[N], fa[N], dep[N], wson[N];
void dfs1(int x){
    siz[x] = 1;
    for(int i = head[x];i;i = nxt[i]) if(to[i] != fa[x]){
        dep[to[i]] = dep[x] + 1;
        fa[to[i]] = x; dfs1(to[i]);
        siz[x] += siz[to[i]];
        if(siz[wson[x]] < siz[to[i]]) wson[x] = to[i];
    }
}
int dfn[N], pre[N], top[5][N], tim;
void dfs2(int x, int tp){
    dfn[x] = ++tim; pre[tim] = x; top[0][x] = tp;
    for(int i = 1;i < 5;++ i) top[i][x] = top[i-1][fa[top[i-1][x]]];
    if(wson[x]){
        dfs2(wson[x], tp);
        for(int i = head[x];i;i = nxt[i])
            if(to[i] != fa[x] && to[i] != wson[x])
                dfs2(to[i], to[i]);
    }
}
int ask(int x, int k){
    for(int i = 4;~i;-- i)
        if(k > dep[x] - dep[top[i][x]]){
            k -= dep[x] - dep[top[i][x]] + 1;
            x = fa[top[i][x]];
        }
    return pre[max(1, dfn[x] - k)];
}
int lca(int u, int v){
    while(top[0][u] != top[0][v]){
        if(dep[top[0][u]] < dep[top[0][v]]) swap(u, v);
        u = fa[top[0][u]];
    } return dep[u] < dep[v] ? u : v;
}
int dis(int u, int v){
    if(u == -1 || v == -1) return 0;
    return dep[u] + dep[v] - (dep[lca(u,v)]<<1);
}
struct Node {
    int u, v, len;
    Node(int _u = -1, int _v = -1, int _len = 0): u(_u), v(_v), len(_len){}
    Node operator + (const Node &o) const {
        if(u == -1) return o; if(o.u == -1) return *this;
        Node res = len > o.len ? *this : o;
        int tmp = dis(u, o.u); if(chmax(res.len, tmp)){res.u = u; res.v = o.u;}
        tmp = dis(u, o.v); if(chmax(res.len, tmp)){res.u = u; res.v = o.v;}
        tmp = dis(v, o.u); if(chmax(res.len, tmp)){res.u = v; res.v = o.u;}
        tmp = dis(v, o.v); if(chmax(res.len, tmp)){res.u = v; res.v = o.v;}
        return res;
    }
} seg[N<<2];
void upd(int x, int L, int R, int p, bool op){
    if(L == R){seg[x].u = seg[x].v = op ? pre[L] : -1; return;}
    int mid = L + R >> 1;
    if(p <= mid) upd(x<<1, L, mid, p, op);
    else upd(x<<1|1, mid+1, R, p, op);
    seg[x] = seg[x<<1] + seg[x<<1|1];
}
Node qry(int x, int L, int R, int l, int r){
    if(l <= L && R <= r) return seg[x];
    int mid = L + R >> 1;
    if(r <= mid) return qry(x<<1, L, mid, l, r);
    if(mid < l) return qry(x<<1|1, mid+1, R, l, r);
    return qry(x<<1, L, mid, l, r) + qry(x<<1|1, mid+1, R, l, r);
}
int tr[N];
void upd(int p, int v){for(;p <= n;p += p & -p) tr[p] += v;}
int qry(int p){int r = 0; for(;p;p -= p & -p) r += tr[p]; return r;}
multiset<pii> S;
bool calc(){
    int d; read(d); int l = S.rbegin()->se, u = ask(l, d), v = ask(u, d);
    if(qry(dfn[v]+siz[v]-1) - qry(dfn[v]-1) != tcnt) return false;
    Node tmp = qry(1, 1, n, dfn[v], dfn[v]+siz[v]-1);
    return dis(tmp.u, u) <= d && dis(tmp.v, u) <= d;
}
int main(){
    read(n); read(q);
    for(int i = 1, u, v;i < n;++ i){
        read(u); read(v); add(u, v); add(v, u);
    } fa[1] = 1; dfs1(1); dfs2(1, 1);
    while(q --){
        int op; read(op);
        if(op <= 2){
            int u, v; read(u); read(v);
            int w = lca(u, v); pii pt = MP(dep[w], w);
            if(op == 1){
                if(!ocr[w]) upd(1, 1, n, dfn[w], true); ++ ocr[w];
                upd(dfn[u], 1); upd(dfn[v], 1); upd(dfn[w], -1); ++ tcnt;
                S.insert(pt);
            } else {
                -- ocr[w]; if(!ocr[w]) upd(1, 1, n, dfn[w], false);
                upd(dfn[u], -1); upd(dfn[v], -1); upd(dfn[w], 1); -- tcnt;
                S.erase(S.find(pt));
            }
        } else puts(calc() ? "Yes" : "No");
    }
}

*CF1458D Flip and Reverse

题目描述:给定长为 \(n\)\(01\) 字符串 \(S\),每次你可以选择一段 \(01\) 个数相同的子串,然后反转并翻转,求能得到的字典序最小的字符串。

数据范围:\(\sum n\le 5\cdot 10^5\)

神奇转化.jpg

solution

\(0\)\(-1\)\(1\)\(+1\),然后做前缀和 \(s\),则 \(s_{i-1}\)\(s_i\) 连边,操作即为将一个环的边反向。

然后发现操作前后的边集不变,同时原图任意一个环的两种顺序都可以操作到(证明略),于是直接求字典序最小的欧拉路径即可,直接贪心,时间复杂度 \(O(n)\)

*CF1446F Line Distance

题目描述:给定 \(n\) 个点 \((x_i,y_i)\),求所有点对连成的直线到原点的距离的第 \(k\) 小值。

数据范围:\(n\le 10^5\)

神奇套路.pdf

solution

二分答案 \(d\),问题就转化为有多少个直线与 \(x^2+y^2=d^2\) 有交。

结论:圆外两点连成的直线 \(AB\) 与圆 \(C\) 有交 \(\Leftrightarrow\) \(A,B\)\(C\) 的切点弦不严格相交。

然后直接扫描线+树状数组即可,时间复杂度 \(O(n\log n)\)

AGC014F Strange Sorting

题目描述:给定长度为 \(n\) 的排列,每次操作将前缀最大值按顺序丢到后面。求多少次操作后排列升序。

数据范围:\(n\le 2\times 10^5\)

神奇结论.png

solution

从大到小考虑每个数 \(i\),设当前已经加入 \((i,n]\) 这些数,\(q_i\)\(i\) 的位置(即逆排列),\(f_i\) 表示 \([i,n]\) 排好序所需的操作次数,若 \(f_i>0\)\(g_i\) 表示 \(f_i-1\) 次操作之后 \([i,n]\) 中最前的数。定义前缀最大值的元素为 high,其他的为 low

\(f_{i+1}=0\),则若 \(q_i>q_{i+1}\)\(f_i=1,g_i=i+1\),否则 \(f_i=0\)

\(f_{i+1}>0\),则可以得到 \(g_{i+1}>i+1\)(因为若 \(g_{i+1}=i+1\) 则已排序或至少需两次操作),并且若 \(f_{i+1}-1\) 次操作后 \(i\)\(i+1\) 之前,则 \(f_i=f_{i+1},g_i=g_{i+1}\),否则 \(f_i=f_{i+1}+1,g_i=i+1\)

结论 1:在前 \(f_{i+1}\) 次操作中,若 \(g_{i+1}\) 不在第一个位置,则 \(g_{i+1}\)low

考虑反证法,若某一次操作中 \(g_{i+1}\) 不在第一个位置且是 high,设第一个数为 \(y\),此次操作中是 \(g_{i+1}\) 之前的 high。以后的操作中 \(y\)high \(\Rightarrow g_{i+1}\)high,即 \(y\) 始终在 \(g_{i+1}\) 之前,矛盾。Q.E.D.

结论 2:在前 \(f_{i+1}\) 次操作中,\(g_{i+1},i,i+1\) 的循环顺序不会变。

考虑分类讨论,对于 \([i,n]\) 这些数组成的序列:

  1. \(i\) 是第一个元素:

    1. \(i+1\) 是第二个元素:\(i,i+1\)high\(g_{i+1}\)low

    2. \(g_{i+1}\) 是第二个元素:\(i,g_{i+1}\)high\(i+1\)low

    3. 其他情况:\(i\)high\(i+1,g_{i+1}\)low

  2. \(i+1\) 是第一个元素:\(i+1\)high\(i,g_{i+1}\)low

  3. \(g_{i+1}\) 是第一个元素:\(g_{i+1}\)high\(i,i+1\)low

  4. 其他情况:都是 low

这些情况都不会改变循环顺序,Q.E.D.

由此可得 \(f_{i+1}-1\) 次操作后 \(i\)\(i+1\) 之前 \(\Leftrightarrow\) 初始时循环顺序为 \(g_{i+1},i,i+1\)

#include<bits/stdc++.h>
using namespace std;
const int N = 200003;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, q[N], f[N], g[N];
int main(){
    read(n);
    for(int i = 1, x;i <= n;++ i){read(x); q[x] = i;}
    for(int i = n-1;i;-- i)
        if(f[i+1]){
            if((q[g[i+1]] < q[i]) + (q[i] < q[i+1]) + (q[i+1] < q[g[i+1]]) == 2){
                f[i] = f[i+1]; g[i] = g[i+1];
            } else {
                f[i] = f[i+1] + 1; g[i] = i+1;
            }
        } else if(q[i] > q[i+1]){
            f[i] = 1; g[i] = i+1;
        }
    printf("%d\n", f[1]);
}

AGC017F Zigzag

题目描述:求满足以下条件的 \(m\) 个长为 \(n\)\(01\) 字符串 \(X_i\) 的个数\(\bmod(10^9+7)\)

  • \(k\) 个限制 \((a,b,c)\),表示 \(X_{a,b}=c\)
  • \(\forall i\in[1,m),j\in[1,n]\left[\sum\limits_{k=1}^jX_{i,k}\le\sum\limits_{k=1}^jX_{i+1,k}\right]\)

数据范围:\(n,m\le 20,\text{TL}=4\text s\)

被打傻了.nin

solution

考虑轮廓线 dp,从上一个字符串转移到下一个字符串时,\(dp_{i,S}\) 表示考虑到第 \(i\) 位,新串的前 \(i\) 位和后 \(n-i\) 位的限制组成 \(S\),具体来说就是当旧串的该位为 \(0\) 且新串的该位为 \(1\) 时,把旧串"掰一下":此时旧串前 \(i\) 位的前缀和即为 \(S\)\(i\) 位的前缀和,不需要多记一维来维护这个值了。

此时状态数为 \(O(n2^n)\),转移 \(O(1)\),总时间复杂度 \(O(nm2^n)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 20, mod = 1000000007;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, m, k, lim, o, ans, t[N][N], f[2][1<<N];
void qmo(int &x){x += x >> 31 & mod;}
int main(){
    read(n); read(m); read(k); -- n; memset(t, -1, sizeof t);
    while(k --){
        int a, b, c;
        read(a); read(b); read(c);
        t[a-1][b-1] = c;
    } f[0][0] = 1; lim = 1<<n;
    for(int i = 0;i < m;++ i)
        for(int j = 0;j < n;++ j){
            o ^= 1; memset(f[o], 0, lim<<2);
            for(int S = 0;S < lim;++ S) if(f[!o][S]){
                int val = f[!o][S] - mod;
                if(t[i][j] != 1 && !(S >> j & 1)) qmo(f[o][S] += val);
                if(t[i][j])
                    if(S >> j & 1) qmo(f[o][S] += val);
                    else {
                        int T = S >> j; T &= T - 1;
                        qmo(f[o][(T+1 << j) | (S & (1<<j)-1)] += val);
                    }
            }
        }
    for(int i = 0;i < lim;++ i) qmo(ans += f[o][i] - mod);
    printf("%d\n", ans);
}

-CF1446D2 Frequency Problem (Hard Version)

题目描述:给定长为 \(n\) 的正整数序列 \(a\),求众数不唯一的子串长度的最大值。

数据范围:\(a_i\le n\le 2\cdot 10^5\)

CF1466I The Riddle of the Sphinx

这事一道交互题

题目描述:给定正整数 \(n,b\),交互器有 \(n\) 个自然数 \(a_i<2^b\)。每次询问 \(i,x\),交互器回答 \([a_i>x]\)。求 \(a_i\) 的最大值。

数据范围:\(n,b\le 200\),询问次数 \(\le 3(n+b)\)。交互器是自适应的。

这辈子都打不会交互的(

solution

考虑遍历一遍这些元素,并维护一个栈和当前最大前缀。满足:

  • 栈中(自底向上)第 \(k\) 个元素的前 \(k\)\(\le\) 最大前缀的前 \(k\) 位。
  • 栈中至少有一个元素 \(\ge\) 最大前缀。

考虑加入当前元素 \(p\) 时,若当前栈中有 \(m\) 个元素,并且最大前缀的长度为 \(m\)

  1. \(p\) 的前 \(m\) 位大于最大前缀,则删去最大前缀的最后一位,并弹出栈顶,重复此操作。
  2. \(p\) 的前 \(m\) 位不大于最大前缀且 \(m<b\),若进行过操作 1 则此时最大前缀的下一位必定是 1,否则查询一次来确定最大前缀的下一位,然后在栈中加入 \(p\),最大前缀加上下一位。

做完之后,若栈中仍然存有 \(m\) 个元素,则实际的最大前缀可能比已知的最大前缀更大。应当再检查一次栈中的每个元素。

自栈顶向下考虑,若当前栈中有 \(m\) 个元素,栈中第 \(k\) 个元素的前 \(m\)\(>\) 最大前缀,说明当前的最大前缀是假的,将最大前缀删至 \(k\) 位,删除第 \(k\) 个元素以上的所有元素。

最后做完之后,已知的最大前缀就是确定了的,并且只有目前栈中元素可能成为最大值。递归处理即可。

估计询问次数:每次加入元素询问 \(2\) 次,删除元素询问 \(1\) 次,至多 \(3n\) 次操作。若确定了长度为 \(k\) 的前缀,则下一轮至多 \(3k\) 次操作。由于答案位数为 \(b\),所以总共不超过 \(3(n+b)\) 次。

#include<bits/stdc++.h>
#define PB emplace_back
#define PO pop_back
using namespace std;
typedef vector<int> vi;
int n, b; string ans, opt;
bool qry(int pos, const string &now, bool op){
    cout << pos << " " << ans << now;
    for(int i = ans.size() + now.size();i < b;++ i) cout << op ? '1' : '0';
    cout << endl; cin >> opt; return opt[0] == 'y';
}
void solve(vi id){
    if(ans.size() == b) return;
    if(!id.size() == b){
        while(ans.size() < b) ans += "0"; return;
    } vi stk; string cur = qry(id[0], "0", 1) ? "1" : "0";
    bool flg; stk.PB(id[0]);
    for(int i = 1, p;i < id.size();++ i){
        p = id[i]; flg = false;
        while(!stk.empty() && qry(p, cur, 1)){
            cur.PO(); stk.PO(); flg = true;
        } if(ans.size() + cur.size() == b) continue;
        cur += flg || qry(p, cur + "0", 1) ? "1" : "0"; stk.PB(p);
    } for(int i = stk.size()-1;~i;-- i)
        if(qry(stk[i], cur, 1))
            while(stk.size() > i+1){
                cur.PO(); stk.PO();
            }
    ans += cur; solve(stk);
}
int main(){
    ios::sync_with_stdio(false);
    cin >> n >> b; vector<int> tmp;
    for(int i = 1;i <= n;++ i) tmp.PB(i);
    solve(tmp); cout << "0 " << ans << endl;
}

AGC036D Negative Cycle

题目描述:给定 \(n\) 个点的图,有三类边 (1) \((i\rightarrow i+1,0)\) (2) \((i\rightarrow j,-1)\),其中 \(i<j\) (3) \((i\rightarrow j,1)\),其中 \(i>j\)。后两种边可以断掉,断掉的代价是 \(A_{i,j}\),求最小代价使得图中无负环。

数据范围:\(n\le 500,1\le A_{i,j}\le 10^9\)

solution

无负环 \(\Leftrightarrow\) 存在最短路

具体来说是存在点权 \(p_i\),使得其满足松弛条件。容易发现 \(p_i\) 单调递减,将 \(p_i\) 按值域分块,负边不能连同一个块,正边只能连同块和相邻两块。

\(f_{i,j}\) 表示考虑前 \(i\) 个点,当前最后段是 \((j,i]\),枚举下一段 \((i,k]\),用二维前缀和优化,时间复杂度 \(O(n^3)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 503;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n; LL a[2][N][N], f[N][N], ans;
int main(){
    memset(f, ~0x3f, sizeof f); read(n);
    for(int i = 1;i <= n;++ i)
        for(int j = 1;j <= n;++ j)
            if(i != j) read(a[i>j][i][j]);
    for(int _ = 0;_ < 2;++ _)
        for(int i = 1;i <= n;++ i)
            for(int j = 1;j <= n;++ j)
                a[_][i][j] += a[_][i-1][j] + a[_][i][j-1] - a[_][i-1][j-1];
    for(int i = 1;i <= n;++ i){
        f[i][0] = a[1][i][i];
        for(int j = 0;j < i;++ j)
            for(int k = i+1;k <= n;++ k)
                chmax(f[k][i], f[i][j] + a[0][i][k] - a[0][i][i] + a[1][k][k] - a[1][i][k] - a[1][k][j] + a[1][i][j]);
    }
    for(int i = 0;i < n;++ i) chmax(ans, f[n][i]);
    printf("%lld\n", a[0][n][n] + a[1][n][n] - ans);
}

-AGC036E ABC String

题目描述:给定字符串 \(S\),求满足以下条件的最长的字符串 \(x\)

  • \(x\)\(S\) 的子序列;
  • \(x\)ABC 出现次数相同;
  • \(x\) 中连续两个字符不同。

数据范围:\(|S|\le 10^6,\Sigma=\{A,B,C\}\)

PE530 GCD of Divisors

题目描述:设 \(f(n)=\sum_{d|n}\gcd(d,\frac nd),S(n)=\sum_{k=1}^nf(k)\),求 \(S(10^{15})\)

solution

\[\begin{aligned} S(n)&=\sum_{ij\le n}\gcd(i,j) \\ &=\sum_{d=1}^{\lfloor\sqrt n\rfloor}\varphi(d)g(\lfloor\frac n{d^2}\rfloor) \\ g(n)&=\sum_{i=1}^n\lfloor\frac ni\rfloor \end{aligned} \]

时间复杂度 \(O(\sqrt n\log n)\)

PE580 Squarefree Hilbert numbers

题目描述:定义正整数 \(n\) 是 H-数当且仅当 \(n\equiv 1(\text{mod} \ 4)\),定义正整数 \(n\) 是 H-免平方数当且仅当不存在 \(>1\) 的 H-数的平方整除它。求 \(\le 10^{16}\) 的 H-免平方数数量。

被教育了...

*PE553 Power sets of power sets

题目描述:给定正整数 \(n,k\),求有多少个 \(U=[1,n]\cap\N\) 的幂集的子集 \(X\),使得无向图 \(G=(X,E)\) 使得 \(E=\{(Y_1,Y_2)|Y_1\cap Y_2\ne\varnothing\}\)\(k\) 个连通块。

数据范围:\(n=10^4,k=10,\text{mod}=10^9+7\)

solution

这事sergej.samborskij的做法。我还是不会 EGF

\(t(x)=\sum_{n\ge 0}\frac{2^{2^n}}{n!}x^n\)\(U\) 的幂集的子集个数的 EGF,则 \(q(x)=t(x)e^{-x}\) 是满足并为 \(U\)\(U\) 的幂集的子集个数的 EGF,则 \(f(x)=\ln q(x)\) 是满足并为 \(U\) 且连通的 \(U\) 的幂集的子集个数的 EGF,则 \(r_k(x)=\frac{f(x)^k}{k!}\) 是满足并为 \(U\) 且有 \(k\) 个连通块的 \(U\) 的幂集的子集个数的 EGF,\(c_k(x)=\sum_{n\ge 0}\frac{C(n,k)}{n!}x^n=r_k(x)e^x=\frac{(\ln t(x)-x)^k}{k!}e^x\) 是满足有 \(k\) 个连通块的 \(U\) 的幂集的子集个数的 EGF。

然后贴个 MTT 板子就行可是我没有

*PE468 Smooth divisors of binomial coefficients

题目描述:设 \(S_B(n)\) 表示去除 \(n\)\(>B\) 的质因子后剩下的部分,\(F(n)\) 表示 \(\sum_{B=1}^n\sum_{r=0}^nS_B(\binom nr)\)。求 \(F(11111111)\bmod(10^9+993)\)

solution

\(\binom nr\) 相比 \(\binom n{r-1}\) 的质因数分解是均摊 \(O(\log\log n)\) 个单点修改,询问是前缀积之和,用线段树维护即可,时间复杂度 \(O(n\log\log n)\)

PE484 Arithmetic Derivative

题目描述:设 \(n'=n\sum_{p^e||n}\frac ep\),求 \(\sum_{k=2}^{5\cdot10^{15}}\gcd(k,k')\)

solution

草,原来这题就是 powerful number 求积性函数前缀和模板(然而我连它是个积性函数都没看出来

\(F(n)=\gcd(n,n')\)\(n,m\) 是互质的正整数,则 \(F(nm)=\gcd(nm,(nm)')=\gcd(n,nm'+n'm)\gcd(m,nm'+n'm)=\gcd(n,n')\gcd(m,m')=F(n)F(m)\),所以 \(F(n)\) 是积性函数。

\(F(p^e)=p^{e-[e\bmod p]}\),则 \(F(p)=1\),构造 \(G(n)=1,H=F/G=F*\mu\),所以 \(H(p^e)=F(p^e)-F(p^{e-1})\),且 \(H(n)\ne 0\Rightarrow n\) 是 Powerful number,且

\[\sum_{k=1}^nF(k)=\sum_{k=1}^nH(k)\lfloor\frac nk\rfloor \]

直接暴搜 Powerful Number 的质因数分解即可,时间复杂度 \(O(\sqrt n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 70777777, M = 4157410;
LL n, ans; int sqr, pri[M], tot; bool notp[N];
void init(int m){
    notp[0] = notp[1] = true;
    for(int i = 2;i <= m;++ i){
        if(!notp[i]) pri[tot++] = i;
        for(int j = 0;j < tot && i * pri[j] <= m;++ j){
            notp[i * pri[j]] = true;
            if(!(i % pri[j])) break;
        }
    }
}
void dfs(int fr, LL val, LL fun){
    ans += n / val * fun;
    if(fr >= tot || val > n / ((LL)pri[fr] * pri[fr])) return;
    for(int i = fr;i < tot;++ i){
        LL tmp = (LL)pri[i] * pri[i];
        if(val > n / tmp) return;
        for(int j = 2;;++ j, tmp *= pri[i]){
            if(!(j % pri[i])) dfs(i+1, val * tmp, fun * (tmp - tmp / ((LL)pri[i] * pri[i])));
            else if(j % pri[i] > 1) dfs(i+1, val * tmp, fun * (tmp / pri[i] - tmp / ((LL)pri[i] * pri[i])));
            if(val > n / tmp / pri[i]) break;
        }
    }
}
int main(){scanf("%lld", &n); sqr = sqrt(n) + 1; init(sqr); dfs(0, 1, 1); printf("%lld\n", ans-1);}

AGC043D Merge Triplets

题目描述:给定正整数 \(n\) 和质数 \(p\),求可以被 \(n\) 个长度为 \(3\) 的序列归并的排列数量\(\bmod p\)

数据范围:\(n\le 2000,10^8<p\le 10^9+7\)

solution

考虑两个序列归并时,可以分别把两个序列按前缀最大值所在位置割开,形成一段段,把这每一段按开头位置做归并是一样的(这时就变成了真正的归并排序)。

多个序列同理。问题转化为:求有多少个排列 \(P\),使得可以分割为一些长度 \(\le 3\) 的段,且开头位置递增,每一段的最大值是开头。由于要拼成每个序列长度 \(=3\),所以长为 \(2\) 的段的个数 \(\le\) 长为 \(1\) 的段的个数。

再转化一下:设 \(a_1,a_2,\dots,a_k\) 是每段的长度,满足 \(2\) 的个数 \(\le 1\) 的个数,则贡献(排列数)为 \((3n)!/\prod_{i=1}^n\sum_{j=1}^ia_j\)

使用 dp 做,设 \(f_{i,j}\) 表示考虑前 \(i\) 个位置,\(1\) 的个数 \(-\ 2\) 的个数是 \(j\) 的方案数梦回CSP2019,直接做,时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 6005, M = 9005;
int n, m, mod, f[N][M], inv[N], ans;
void qmo(int &x){x += x >> 31 & mod;}
int main(){
    scanf("%d%d", &n, &mod); n *= 3; m = (n>>1)+1; f[0][m] = inv[1] = 1;
    for(int i = 2;i <= n;++ i) inv[i] = mod - (LL) mod / i * inv[mod % i] % mod;
    for(int i = 1;i <= n;++ i){
        for(int j = m-(i>>1);j <= m+i;++ j){
            f[i][j] = f[i-1][j-1];
            if(i >= 2) qmo(f[i][j] += f[i-2][j+1] - mod);
            if(i >= 3) qmo(f[i][j] += f[i-3][j] - mod);
            f[i][j] = (LL) f[i][j] * inv[i] % mod;
        }
    } for(int i = m;i <= m+n;++ i) qmo(ans += f[n][i] - mod);
    for(int i = 2;i <= n;++ i) ans = (LL) ans * i % mod;
    printf("%d\n", ans);
}

AGC043E Topology

题目描述:给定正整数 \(n\),设 \(U=[0,n)\cap\N\),给定 \(U\) 的幂集的子集 \(\mathcal S\),构造闭合折线 \((x_i,y_i)\),使得对于任意 \(S\subseteq U\),设 \(B_S=\{(i+\frac 12,\frac 12)|i\in S\}\),使得该折线能在不触碰 \(B_S\) 的情况下移到 \(y\) 负平面上 \(\Leftrightarrow\) \(S\in\mathcal S\)

数据范围:\(n\le 8,\varnothing\in\mathcal S\)

solution

这是sm人类智慧啊,容易发现 \(\mathcal S\) 有传递性然而这也是充分条件

考虑 spj 如何实现:有一个结论,从这条曲线的最左端按某种方向出发,经过 \((i+\frac 12,1)\) 就写下 \(u_i\),经过 \((i+\frac 12,0)\) 就写下 \(d_i\),则曲线能从 \(B_U\) 中绕出来当且仅当可以每次删去这条曲线的相邻两个相同字符,把它删空。

如果会构造 \(\mathcal S=2^U\backslash\{U\}\),则可以通过枚举所有极小非法集合做一遍,再拼起来。

构造 \(\mathcal S=2^U\backslash\{U\}\) 可以使用递归构造的方法:首先当 \(n=1\) 时顺时针绕一圈,得到 \(u_1d_1\),否则绕过 \(u_1\),递归构造后面的点,得到字符串 \(S\),再穿回来,绕过 \(d_1\),得到字符串 \(S^R\)\(S\) 的翻转),最后回到 \((0,0)\),得到 \(u_1Su_1d_1S^Rd_1\)

*PE715 Sextuplet Norms

题目描述:设 \(f(n)\) 是满足 \(0\le x_i<n\)\(n\bot(\sum x_i^2)\)\(\{x\}_{i=1}^6\) 数量,\(g(n)=\sum_{k=1}^n\frac{f(k)}{k^2\varphi(k)}\),求 \(g(10^{12})\bmod(10^9+7)\)

solution

不容易发现,\(f(n)\) 是积性函数(考虑 \(n\) 的所有质因子 \(\prod p_i^{e_i}\),确定了 \(x_k\bmod p_i^{e_i}\) 之后就确定了 \(x_k\)

于是 \(\frac{f(n)}{n^2\varphi(n)}=\prod\frac{f(p_i^{e_i})}{p_i^{3e_i-1}(p_i-1)}=\prod\frac{p_i^{3e_i-5}}{p_i-1}f(p_i)\),现在要求的就是 \(f(p)\),条件变为 \(p\not|(\sum x_i^2)\),直接上单位根反演,即 \(f(p)=p^6-\frac1p\sum_{a=0}^{p-1}(\sum_{x=0}^{p-1}\omega^{ax^2})^6\)

现在要求的是后面这一项,我们考虑 \((\sum_{x=0}^{p-1}\omega^{ax^2})^2=\sum_{0\le x,y<p}\omega^{a(x^2+y^2)}\)。给定 \(t\),考虑 \(x^2+y^2\equiv t\pmod p\) 的解数。(以下不考虑 \(p=2\)

\(t=0\),即为 \(x=y=0\or (\frac xy)^2\equiv -1\pmod p\),若 \(p\equiv 1\pmod 4\)\(-1\) 有二次剩余,所以有 \(2p-1\) 个解,若 \(p\equiv 3\pmod 4\)\(-1\) 无二次剩余,所以有 \(1\) 个解。

\(t\ne 0\),考虑 \(x^2-y^2=(x+y)(x-y)\equiv -t\pmod p\),其有 \(p-1\) 个解,即 \(\sum_{y=0}^{p-1}(1+\left(\frac{y^2-t}p\right))=p-1\)。若 \(p\equiv 1\pmod 4\)\(-1\) 有二次剩余,所以 \(\sum_{y=0}^{p-1}(1+\left(\frac{-y^2+t}p\right))=p-1\),所以有 \(p-1\) 个解,若 \(p\equiv 3\pmod 4\)\(-1\) 无二次剩余,所以 \(\sum_{y=0}^{p-1}(1-\left(\frac{-y^2+t}p\right))=p-1\),所以有 \(p+1\) 个解。

现在求 \((\sum_{x=0}^{p-1}\omega^{ax^2})^2\),显然 \(a=0\) 时为 \(p^2\)\(a\ne 0\) 时若 \(p\equiv 1\pmod 4\) 则为 \(2p-1+\sum_{x=1}^{p-1}(p-1)\omega^{ax}=p\),若 \(p\equiv 3\pmod 4\) 则为 \(1+\sum_{x=1}^{p-1}(p+1)\omega^{ax}=-p\)。综上,

\[\frac{f(p)}{p^2(p-1)}=\begin{cases}8,&p=2\\p^3-1,&p\equiv1\pmod 4\\p^3+1,&p\equiv3\pmod 4\end{cases} \\ \frac{f(p^e)}{p^{2e}\varphi(p^e)}=p^{3(e-1)}(p^3-\left(\frac{-1}p\right)) \]

\(p^3\)\(\left(\frac{-1}p\right)\) 都是完全积性函数,用 min_25 筛直接做。

-PE707 Lights Out

题目描述:求奇怪 01 矩阵的秩。

solution

结论:\(\text{rank}(w,h)=2^{wh-\text{codim(w,h)}}\),其中 \(\text{codim}(w,h)=\gcd(p_w(x+1),p_h(x))\) 的次数,\(p_0(x)=1,p_1(x)=x,p_{n+1}(x)=xp_n(x)+p_{n-1}(x)\)(定义在 \(\mathbb F_2\) 上)。并且 \(\text{codim}(w,h)\) 是循环数列,周期可以在这里查到(\(w=199\) 时是 \(24600\))然后就能直接上了...

*PE695 Random Rectangles

题目描述:随机 \(6\)\([0,1]\) 之间的数 \(x_0,x_1,x_2,y_0,y_1,y_2\),求 \(|(x_i-x_j)(y_i-y_j)|\) 的中位数的期望值。保留 \(10\) 位小数。

solution

考虑 \(\max\{x_i\}-\min\{x_i\}\) 的期望值是 \(1/2\),所以将矩形缩小到至少两个点在边界上的情况,这个矩形的面积的期望值是 \(1/4\)

此时有两种情况:两个点在角上,一个点在内部;两个点在边上,一个点在角上。前者概率是 \(1/3\),后者概率是 \(2/3\)

对于前者,面积中位数的期望值是

\[\int_0^1\int_0^1\max\{xy,(1-x)(1-y)\}\text dx\text dy=\frac 5{12} \]

对于后者,设角上的点坐标为 \((1,1)\),另外两点坐标是 \((x,0)\)\((0,y)\)。则为

\[\int_0^1\int_0^1\text{medium}\{1-x,1-y,xy\}\text dx\text dy=\text{???} \]

AGC045D Lamps and Buttons

solution

转化题面:设 \(t\) 表示 \([1,A]\) 中最小的位置使得 \(p_t=t\)(若不存在则 \(t=A+1\)),求有多少个排列 \(p\) 满足 \(\forall i\in[A+1,n],\exist j\in[1,t)\) 使得 \(j\)\(i\) 在同一个环里。

枚举 \(t\),则 \(\forall i<t,p_i\ne i\),用容斥做,枚举有 \(j\) 个元素满足 \(i<t,p_i=i\),设 \(a=t-j-1,b=n-A,c=\max(0,A-t)\),则问题转化为:

有多少个长为 \(a+b+c\) 的排列,使得 \(\forall i\in[a+1,a+b],\exist j\in[1,a]\) 使得 \(j\)\(i\) 在同一个环里。

从小到大考虑插入每个点的位置,得到答案是 \((a+b+c)!\times\frac a{a+b}\)

时间复杂度 \(O(n^2+A)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10000003, mod = 1e9 + 7;
int n, A, fac[N], inv[N], ans;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int ksm(int a, int b){
    int res = 1;
    for(;b;b >>= 1, a = (LL)a * a % mod)
        if(b & 1) res = (LL)res * a % mod;
    return res;
}
void init(int m){
    fac[0] = 1;
    for(int i = 1;i <= m;++ i) fac[i] = (LL)fac[i-1] * i % mod;
    inv[m] = ksm(fac[m], mod-2);
    for(int i = m;i;-- i) inv[i-1] = (LL)inv[i] * i % mod;
}
int INV(int x){return (LL)inv[x] * fac[x-1] % mod;}
int C(int n, int m){return (LL)fac[n] * inv[m] % mod * inv[n-m] % mod;}
int calc(int a, int b, int c){return (LL)fac[a+b+c] * a % mod * INV(a+b) % mod;}
void qmo(int &x){x += x >> 31 & mod;}
int main(){
    read(n); read(A); init(n);
    for(int t = 1;t <= A+1;++ t)
        for(int j = 0;j < t;++ j){
            int tmp = (LL)calc(t-j-1, n-A, max(0, A-t)) * C(t-1, j) % mod;
            if(j & 1) qmo(ans -= tmp); else qmo(ans += tmp - mod);
        }
    printf("%d\n", ans);
}

AGC047D Twin Binary Tree

solution

赛场上没做出这题是sm鬼啊

枚举这个环上第一棵树的 lca,在第二棵树的 lca 处断开,枚举左边叶子和第二棵树的祖先打标记,枚举右边叶子和第二棵树的祖先算答案。

时间复杂度 \(O(H^22^H)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1<<18, mod = 1e9 + 7;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int h, l, p[N], sum[N], ans;
void qmo(int &x){x += x >> 31 & mod;}
int main(){
    read(h); l = 1<<h-1;
    for(int i = 0;i < l;++ i) read(p[i]), --p[i];
    for(int d = 0;d < h-1;++ d)
        for(int u = 1<<d;u < (1<<d+1);++ u){
            int L = u<<h-1-d, R = u+1<<h-1-d, mid = L+R>>1;
            for(int i = L;i < mid;++ i){
                int tmp = 1;
                for(int j = i;j != u;j >>= 1) tmp = (LL)tmp * j % mod;
                for(int j = l + p[i-l];j > 1;j >>= 1){
                    tmp = (LL)tmp * j % mod;
                    qmo(sum[j] += tmp - mod);
                }
            }
            for(int i = mid;i < R;++ i){
                int tmp = 1;
                for(int j = i;j != u;j >>= 1) tmp = (LL)tmp * j % mod;
                for(int j = l + p[i-l];j > 1;j >>= 1){
                    tmp = (LL)tmp * j % mod;
                    ans = (ans + (LL)tmp * sum[j^1] % mod * u % mod * (j>>1)) % mod;
                }
            }
            for(int i = L;i < mid;++ i)
                for(int j = l + p[i-l];j > 1;j >>= 1)
                    sum[j] = 0;
        }
    printf("%d\n", ans);
}

AGC048D Pocky Game

题目描述:一行 \(n\) 堆石子,第 \(i\) 堆有 \(a_i\) 个,先手可以在最左边一堆中扔掉至少一个,后手可以在最右边一堆中扔掉至少一个。不能操作的人输,求先手赢还是后手赢。\(T\) 组数据。

数据范围:\(T,n\le 100,a_i\le 10^9\)

solution

有一个很显然但我看不出来的结论:每次只会取一个或取完一堆。

感性理解:仅一堆时显然成立,否则两人在碰面之前都互不影响,只需考虑每一堆取的次数。

然后考虑区间 dp,设 \(f_{i,j}\) 表示仅考虑 \([i,j]\) 这一段,此时为先手先手(?),第 \(k(\ne i)\) 堆的石子数是 \(a_k\),第 \(i\) 堆的石子数至少多少才能使先手赢。\(g_{i,j}\) 同理。

首先两人会一个一个丢,当 \(a_j\) 丢到 \(g_{i+1,j}\) 颗时,若后手继续丢,则先手会直接扔掉第 \(i\) 堆,然后后手必败。

所以若 \(a_j<g_{i+1,j}\),则先手必赢,若 \(a_j\ge g_{i+1,j}\),后手会在 \(a_j-g_{i+1,j}+1\) 轮之后丢掉第 \(j\) 堆,转化为 \(f_{i,j-1}\) 的情况。也即

\[f_{i,j}=\min\{a_i,f_{i,j-1}+a_j-g_{i+1,j}\}+1 \\ g_{i,j}=\min\{a_j,f_{i+1,j}+a_i-f_{i,j-1}\}+1 \]

先手必胜 \(\Leftrightarrow\) \(f_{1,n}\le a_1\)。时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 103;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int T, n, a[N], f[N][N], g[N][N];
void solve(){
    read(n);
    for(int i = 1;i <= n;++ i){read(a[i]); f[i][i] = g[i][i] = 1;}
    for(int l = 1;l < n;++ l)
        for(int i = 1;i <= n-l;++ i){ int j = i+l;
            if(a[j] < g[i+1][j]) f[i][j] = 1;
            else f[i][j] = min(a[i], f[i][j-1] + a[j] - g[i+1][j]) + 1;
            if(a[i] < f[i][j-1]) g[i][j] = 1;
            else g[i][j] = min(a[j], g[i+1][j] + a[i] - f[i][j-1]) + 1;
        }
    puts(f[1][n] <= a[1] ? "First" : "Second");
}
int main(){read(T); while(T --) solve();}

*AGC049D Convex Sequence

solution

做两次差分之后就变成了 \(\sum_{i=1}^{n-1}\binom{n+1-i}2A_i=m-nA_0\),其中 \(A_i\ge 0\)。直接背包即可,时间复杂度 \(O(m\sqrt m)\)

*AGC049E Increment Decrement

题目描述:对于下述问题:

给定长为 \(n\) 的数列 \(A\) 和正整数 \(c\)。对 \(A\) 操作,每一步可以单点 +1 或 -1,花费 \(1\) 的代价,或区间 +1 或 -1,花费 \(c\) 的代价。求将 \(A\) 变为全 \(0\) 的最小代价。

给定 \(n\) 个长为 \(k\) 的数列 \(B_i\) 和正整数 \(c\),求对于所有满足 \(A_i\)\(B_i\) 中任选的 \(k^n\)\(A\) 的答案之和\(\bmod(10^9+7)\)

数据范围:\(k\le n\le 50,c\le 50,B_{i,j}\le 10^9\)

solution

先考虑原问题,设第二种操作给 \(A_i\) 减去 \(D_i\),则花费是

\[\sum_{i=1}^n(C\max\{D_i-D_{i-1},0\}+|A_i-D_i|) \]

考虑 dp,设 \(f_{i,j}\) 表示前 \(i\) 个数,\(D_i=j\) 的情况下的花费最小值。

\[f_{i,j}=\min_{k\ge 0}\{f_{i-1,k}+C\max\{j-k,0\}\}+|j-A_i| \]

结论:设 \(F_i(x)=f_{i,x}\),则 \(F_i(x)\) 是下凸的。

\(G(x)=F_i(x)-|x-A_i|\),因为 \(|x-A_i|\) 是下凸的,所以 \(G(x)\) 是下凸的 \(\Rightarrow\) \(F_i(x)\) 是下凸的。

\(F_{i-1}(x)\) 的最小值取在 \(x=k\),则 \(G(x)=F_{i-1}(k),\forall x\in[0,k]\)

\(x_0\) 是最大的满足 \(F_{i-1}(x_0)-F_{i-1}(x_0-1)<c\) 的值,则 \(G(x)=F_{i-1}(x),\forall x\in(k,x_0]\)\(G(x)=F_{i-1}(x_0)+C(x-x_0),\forall x\in(x_0,+\infty)\)

\(G(x)\) 被分为 \(3\) 个部分,第一部分是斜率为 \(0\) 的直线,第二部分是斜率为 \([1,c-1]\) 的折线,第三部分是斜率为 \(c\) 的直线。Q.E.D.

也可以发现 \(G(x)\) 是一个折线,按照维护折线的通常思路,设 \(X_i\) 是斜率为 \(i\) 的线段的起始点,通过维护 \(X_i\) 来维护 \(G(x)\)

考虑每次转移,先加上 \(|x-A_i|\),相当于 \(A_i\) 左边折线的斜率 -1,右边折线的斜率 +1,也就是 \(X\) 中插入两个 \(A_i\)

然后是里面那个 \(\min\) 的柿子,相当于把斜率为 \(-1\)\(c+1\) 的线段去掉,也就是删除 \(X\) 的最小值和最大值。

于是原问题可以这么做:

  1. 维护可重集 \(S\),初始时有 \(c\)\(0\)
  2. 从小到大枚举 \(i\),给答案加上 \(A_i-\min S\),然后在 \(S\) 中插入两个 \(A_i\),删去最小值和最大值。

md怎么好像见过

然后考虑计算答案,枚举值 \(x\),考虑 \(<x\) 的数被作为最小值被删除过多少次。因为只关心大小,所以将 \(<x\) 的看作 \(0\)\(\ge x\) 的看作 \(1\)\(f_{i,j}\) 表示前 \(i\) 个数,刚加入两个 \(A_i\) 之后 \(S\) 中有恰好 \(j\)\(1\) 的方案数。直接 dp,时间复杂度 \(O(n^2k(k+c))\).

#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 51, mod = 1e9 + 7;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, c, k, b[N][N], pw[N], lst, ans, pos[N], dp[N][N];
vector<int> tmp;
int main(){
	read(n); read(c); read(k);
	for(int i = 0;i < n;++ i){
		for(int j = 0;j < k;++ j){
			read(b[i][j]); tmp.PB(b[i][j]);
		} sort(b[i], b[i] + k);
	} sort(tmp.begin(), tmp.end());
	tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());
	pw[0] = 1;
	for(int i = 1;i <= n;++ i) pw[i] = (LL)pw[i-1] * k % mod;
	for(int x : tmp){
		memset(dp, 0, sizeof dp);
		for(int i = 0;i < n;++ i)
			pos[i] = lower_bound(b[i], b[i] + k, x) - b[i];
		dp[0][0] = 1;
		for(int i = 0;i < n;++ i)
			for(int j = 0;j <= i && j <= c;++ j){
				int nxt = max(0, j-1);
				dp[i+1][nxt] = (dp[i+1][nxt] + (LL)dp[i][j] * pos[i]) % mod;
				nxt = j+1;
				if(nxt <= c) ans = (ans + (LL)(x-lst) * pw[n-i-1] % mod * (k-pos[i]) % mod * dp[i][j]) % mod;
				else nxt = c;
				dp[i+1][nxt] = (dp[i+1][nxt] + (LL)dp[i][j] * (k - pos[i])) % mod;
			}
		lst = x;
	} printf("%d\n", ans);
}

-AGC041D Problem Scores

题目描述:求长为 \(n\) 的正整数序列 \(A_i\) 数量 \(\bmod p\),满足:

  • \(1\le A_1\le\dots\le A_n\le n\)
  • \(\forall k\in[1,n)\),有 \(A\) 中任意 \(k\) 个数之和 \(<\) 任意 \(k+1\) 个数之和。

数据范围:\(n\le 5000,9\times 10^8<p<10^9\)\(p\) 是质数。

AGC039E Pairing Points

题目描述:圆上有 \(n\) 个点,其中一些点对之间有连线,保证不存在三线共点。求有多少种选出其中 \(n/2\) 条线的方法,使得每个点恰好连一条线,且这 \(n/2\) 条线形成一棵树。

数据范围:\(n\le 40,n\) 是偶数。

我谔谔

solution

考虑 dp,设 \(f_{l,i,r}\) 表示考虑 \([l,r]\) 这些点,且 \(i\) 向外界连一条边时的方案数,其中 \(l\equiv r\pmod 2\)。特别的,\(f_{i,i,i}=1\)

枚举与 \(1\) 匹配的点 \(i\),则方案数为 \(f_{2,i,n}\)

由于不能不连通,当 \(l<i<r\) 时必定存在一些两两不交的边,和 \(i\) 连出的边有交。设这些边中最远离 \(i\) 的是 \(j\)\(k\) 之间的边。

\((j,i)\) 不能向 \((k,r]\) 连边,且必定存在 \(p\) 使得 \((j,p]\)\([l,j)\) 连边,\((p,i)\)\((i,k)\) 连边,对面同理有 \(q\)。则

\[f_{l,i,r}=\sum\{f_{l,j,p}f_{p+1,i,q-1}f_{q,k,r}\mid a_{j,k}=1\land l\le j\le p<i<q\le k\le r\} \]

稍微优化一下,设 \(g_{l,k,p}=\sum f_{l,j,p}[a_{j,k}=1]\),则 \(f_{i,l,r}=\sum f_{p+1,i,q+1}\sum g_{l,k,p}f_{q,k,r}\),其中后面的和式与 \(i\) 无关。时间复杂度 \(O(n^5)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 42;
int n;
LL f[N][N][N], g[N][N][N], ans;
char str[N];
bool a[N][N];
int main(){
    scanf("%d", &n); n <<= 1;
    for(int i = 1;i <= n;++ i){
        scanf("%s", str + 1);
        for(int j = 1;j <= n;++ j)
            a[i][j] = str[j] == '1';
    }
    for(int i = 2;i <= n;++ i){
        f[i][i][i] = 1;
        for(int j = 2;j <= n;++ j)
            if(a[i][j]) g[i][j][i] += f[i][i][i];
    }
    for(int len = 2;len < n;len += 2)
        for(int l = 2;l <= n - len;++ l){
            int r = l + len;
            for(int p = l;p <= r-2;++ p)
                for(int q = p+2;q <= r;++ q){
                    LL tmp = 0;
                    for(int k = q;k <= r;++ k) tmp += g[l][k][p] * f[q][k][r];
                    for(int i = p+1;i < q;++ i) f[l][i][r] += tmp * f[p+1][i][q-1];
                }
            for(int i = l;i <= r;++ i)
                for(int j = 2;j <= n;++ j)
                    if(a[i][j]) g[l][j][r] += f[l][i][r];
        }
    for(int i = 2;i <= n;++ i)
        if(a[1][i]) ans += f[2][i][n];
    printf("%lld\n", ans);
}

AGC039F Min Product Sum

题目描述:对于 \(n\times m\) 的矩阵,定义函数 \(f(x,y)\) 为第 \(x\) 行和第 \(y\) 列共 \(n+m-1\) 个数的最小值。

对于 \(n\times m\) 的矩阵,定义其权值是 \(\prod_{x=1}^n\prod_{y=1}^mf(x,y)\)

给定 \(n,m,k,p\),求所有 \(n\times m\),值域为 \([1,k]\) 的矩阵的权值和\(\bmod p\)

数据范围:\(n,m,k\le 100,10^8<p<10^9\)\(p\) 是质数。

solution

\(x_i\) 是第 \(i\) 行的最小值,\(y_i\) 是第 \(i\) 列的最小值,则权值为 \(\prod_{i=1}^n\prod_{j=1}^m\min(x_i,y_j)\)

计算这个可以将 \(x_i,y_i\) 混在一起排序,从小到大加入每个元素,若当前已经加入 \(i\)\(j\) 列,则加入一行 \(t\) 的贡献是 \(t^{m-j}\),一列 \(t\) 的贡献是 \(t^{n-j}\)

然后考虑固定 \(x_i,y_i\) 之后,\(A_{i,j}\ge\max\{x_i,y_j\}\),且要求每一行、列的最小值恰好是 \(x_i,y_j\)

使用容斥原理,用 \((\ge x_i)-(\ge x_i+1)=(=x_i)\),枚举 \(c\)\(d\) 列'斥',则这些行列的限制变为 \(\ge x_i+1\)\(\ge y_j+1\),且带上 \((-1)^{c+d}\) 的系数。

然后就可以 dp 了,设 \(f_{t,i,j}\) 是已经加入了 \(i\)\(j\) 列,这些列是 \(x_i,y_j\le t\) 的,设 \(g_{i,j}\) 表示上个 dp 状态。

\[f_{t,i+a,j}\leftarrow g_{i,j}\times\binom{n-i}a\times t^{a(m-j)}\times (k+1-t)^{aj} \\ f_{t,i,j+b}\leftarrow g_{i,j}\times\binom{n-j}b\times t^{(n-i)b}\times (k+1-t)^{ib} \\ f_{t,i+c,j}\leftarrow g_{i,j}\times\binom{n-i}c\times t^{c(m-j)}\times (k-t)^{cj}\times (-1)^c \\ f_{t,i,j+d}\leftarrow g_{i,j}\times\binom{n-j}b\times t^{(n-i)d}\times (k-t)^{id}\times (-1)^d \]

分别表示行'容'、列'容'、行'斥'、列'斥'。

使用滚动数组优化,时间复杂度 \(O(nmk(n+m))\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 103;
int n, m, k, mod, f[2][N][N], C[N][N], pw[N][N];
void qmo(int &x){x += x >> 31 & mod;}
int main(){
    scanf("%d%d%d%d", &n, &m, &k, &mod);
    if(n < m) swap(n, m);
    for(int i = 0;i <= n;++ i){
        C[i][0] = 1;
        for(int j = 1;j <= i;++ j)
            qmo(C[i][j] = C[i-1][j-1] + C[i-1][j] - mod);
    }
    for(int i = 0;i <= k;++ i){
        pw[i][0] = 1;
        for(int j = 1;j <= n;++ j)
            pw[i][j] = (LL)pw[i][j-1] * i % mod;
    } int o = 0; f[0][0][0] = 1;
    for(int t = 1;t <= k;++ t, o ^= 1){
        memset(f[!o], 0, sizeof f[!o]);
        for(int i = 0;i <= n;++ i)
            for(int j = 0;j <= m;++ j) if(f[o][i][j]){
                int t1 = f[o][i][j], t2 = (LL)pw[t][m-j] * pw[k+1-t][j] % mod;
                for(int a = 0;i + a <= n;++ a, t1 = (LL)t1 * t2 % mod)
                    f[!o][i+a][j] = (f[!o][i+a][j] + (LL)C[n-i][a] * t1) % mod;
            }
        o ^= 1; memset(f[!o], 0, sizeof f[!o]);
        for(int i = 0;i <= n;++ i)
            for(int j = 0;j <= m;++ j) if(f[o][i][j]){
                int t1 = f[o][i][j], t2 = (LL)pw[t][n-i] * pw[k+1-t][i] % mod;
                for(int b = 0;j + b <= m;++ b, t1 = (LL)t1 * t2 % mod)
                    f[!o][i][j+b] = (f[!o][i][j+b] + (LL)C[m-j][b] * t1) % mod;
            }
        o ^= 1; memset(f[!o], 0, sizeof f[!o]);
        for(int i = 0;i <= n;++ i)
            for(int j = 0;j <= m;++ j) if(f[o][i][j]){
                int t1 = f[o][i][j], t2 = mod - (LL)pw[t][m-j] * pw[k-t][j] % mod;
                for(int c = 0;i + c <= n;++ c, t1 = (LL)t1 * t2 % mod)
                    f[!o][i+c][j] = (f[!o][i+c][j] + (LL)C[n-i][c] * t1) % mod;
            }
        o ^= 1; memset(f[!o], 0, sizeof f[!o]);
        for(int i = 0;i <= n;++ i)
            for(int j = 0;j <= m;++ j) if(f[o][i][j]){
                int t1 = f[o][i][j], t2 = mod - (LL)pw[t][n-i] * pw[k-t][i] % mod;
                for(int d = 0;j + d <= m;++ d, t1 = (LL)t1 * t2 % mod)
                    f[!o][i][j+d] = (f[!o][i][j+d] + (LL)C[m-j][d] * t1) % mod;
            }
    } printf("%d\n", f[o][n][m]);
}

*AGC041E Balancing Network

题目描述:给定正整数 \(n\)\(m\) 个二元组 \((x_i,y_i)\),有一个初始为 \(p_i=i\) 的序列,按顺序考虑所有二元组 \((x,y)\),做操作:把所有 \(x\) 变成 \(y\) 或把所有 \(y\) 变成 \(x\) 。构造操作序列,使得最终 \(p\) 中 (1) 只有一种数 (2) 不只有一种数。

数据范围:\(n\le 5\cdot10^4,m\le 10^5,1\le x_i<y_i\le n\)

solution

首先做第一问,设 \(f_{i,j,t}\) 表示考虑 \([i,n]\) 这些二元组,从 \(j\) 开始能不能到 \(t\),转移方程是 \(f_{i,x_i,t}=f_{i,y_i,t}=f_{i+1,x_i,t}\or f_{i+1,y_i,t}\),其他值不变。使用 bitset 优化,找到一个合法 \(t\) 之后再做一遍 dp 即可。时间复杂度 \(O(nm/w)\)

然后考虑第二问,设 \(f_{i,j}\) 表示考虑 \([i,n]\) 这些二元组,从 \(j\) 开始会到哪儿,\(g_{i,j}\)\(f_i\) 的桶。则转移方程是 \(f_{i,x_i}=f_{i,y_i}=f_{i+1,x_i}\)\(f_{i+1,y_i}\),比较一下这两个值的出现次数,把较大的扔给较小的,就是 \(g\) 的某个位置 -1,某个位置 +1。显然当 \(n>2\)\(m=0\) 时一定可以保证 \(g\) 中有两个位置 \(>0\)

AGC040E Prefix Suffix Addition

题目描述:给定长为 \(n\) 的正整数序列 \(a_i\) 和长为 \(n\),初始为 \(0\) 的序列 \(x_i\)。每次操作给 \(x\) 的前缀加上不降序列,或者给 \(x\) 的后缀加上不升序列。求把 \(x\) 变为 \(a\) 的最小操作次数。

数据范围:\(n\le 2\cdot10^5,a_i\le 10^9\)

solution

\(A_i\) 拆分成 \(x_i+y_i\),其中 \(x\) 由操作 1 做出,\(y\) 由操作 2 做出,则操作次数是 \(\sum_{i=1}^n[x_i>x_{i+1}]+\sum_{i=0}^{n-1}[y_i<y_{i+1}]\)

从前往后考虑,设 \(f_{i,j}\) 表示前 \(i\) 个数,\(x_i=j\) 时的最小操作次数。有

\[f_{i,k}\leftarrow f_{i-1,j}+[k<j]+[k<j+a_i-a_{i-1}] \]

发现 \(f_{i,j}\) 关于 \(j\) 不升,且 \(f_{i,0}\le f_{i,a_i}+2\)。所以只需要存下 \(f_{i,a_i}\)\(f_{i,j-1}>f_{i,j}\) 的至多两个 \(j\) 即可。单次转移 \(O(1)\),时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 200003;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, x, lst, ans, v[2], p[2], nxt[2];
int main(){
    read(n);
    for(int _ = 0;_ <= n;++ _){
        if(_ < n) read(x); else x = 0;
        p[0] = x - lst; p[1] = 0;
        if(p[0] < p[1]) swap(p[0], p[1]);
        nxt[0] = max(0, v[0] + p[0]);
        nxt[1] = max(0, min(v[1] + p[0], v[0] + p[1]));
        if(nxt[0] > x){++ ans; v[0] = nxt[1]; v[1] = 0;}
        else {v[0] = nxt[0]; v[1] = nxt[1];}
        lst = x;
    } printf("%d\n", ans);
}

Code Festival 2016 qual A

E LRU Puzzle

题目描述:给定 \(n\) 个初始为 \((1,2,\dots,m)\) 的排列和长为 \(q\) 的正整数序列 \(a_i\),第 \(i\) 次操作选择任意一个排列,将 \(a_i\) 移动到开头。求最终状态的这 \(n\) 个排列是否有可能相同。

数据范围:\(n,m,q\le 10^5,a_i\le m\)

solution

如果不考虑多个排列,操作即为未操作的数按顺序放在最后,从后往前扫描操作序列,按顺序把第一个出现的数放在最前,多次出现的可以忽略。

然后有多个排列之后,发现这个最终排列必须是把操作序列整个做一遍后得到的排列,设为 \(b\)

\(p\) 是最大的满足 \(b_p>b_{p+1}\) 的正整数,则 \([1,p]\) 这一段是每个序列都必需的操作序列。

再扫一遍即可,时间复杂度 \(O(m+q)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 100003;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, m, q, a[N], cnt[N], pos[N], v[N], tt, ans; bool vis[N];
int main(){
    read(n); read(m); read(q);
    for(int i = 1;i <= q;++ i) read(a[i]);
    for(int i = q;i;-- i)
        if(!vis[a[i]]){v[++tt] = a[i]; vis[a[i]] = true;}
    for(int i = 1;i <= m;++ i)
        if(!vis[i]) v[++tt] = i;
    for(int i = 1;i <= m;++ i) pos[v[i]] = i; cnt[0] = n;
    int p = m;
    while(p > 1 && v[p-1] < v[p]) -- p;
    for(int i = q;i;-- i)
        if(cnt[pos[a[i]] - 1]){
            -- cnt[pos[a[i]] - 1];
            ++ cnt[pos[a[i]]];
        }
    for(int i = p-1;i <= m;++ i) ans += cnt[i];
    puts(ans >= n ? "Yes" : "No");
}

Code Festival 2016 Final

*G Zigzag MST

题目描述:给定 \(n\) 个点,编号 \(0,1,\dots,n-1\),给定 \(q\) 个三元组 \((a,b,c)\),表示对 \(\forall k\in\N\)\((a+k)\bmod n\)\((b+k)\bmod n\) 连边权为 \(c+2k\) 的边,\((a+k+1)\bmod n\)\((b+k)\bmod n\) 连边权为 \(c+2k+1\) 的边。求最小生成树的权值和。

数据范围:\(n,q\le 2\times 10^5\)

solution

根据 Kruskal 的性质,若有两条边 \((u,v_1)\)\((u,v_2)\),且前者的边权 \(<\) 后者,则考虑第二条边时 \(u\)\(v_1\) 必定连通,可以改为 \((v_1,v_2)\)

可以改成 \(a\)\(b\) 连边权为 \(c\) 的边,\((a+k)\bmod n\)\((a+k+1)\bmod n\) 连边权为 \(c+2k+1\) 的边,\((b+k)\bmod n\)\((b+k+1)\bmod n\) 连边权为 \(c+2k+2\) 的边。

然后就做完了,时间复杂度 \(O(m\log m)\)

-H Tokaido

题目描述:给定排成一排的 \(n\) 个自然数 \(A_i\),Snuke 和 Rng 玩下列游戏:

  1. 初始时 Snuke 在第 \(1\) 个正整数上,Rng 在第 \(2\) 个上。
  2. 较左的人向右移动到另一个正整数上(不能跟对方在同一位置),重复此步骤,直到无法操作。
  3. 两人的分数分别是经过过的正整数之和。

两人想最大化自己的分数。先给定 \(A_1,A_2,\dots,A_{n-1}\),然后 \(m\) 次询问给定 \(A_n\),求 (Snuke 的分数)-(Rng 的分数)。

数据范围:\(n,m\le 2\cdot10^5,\sum_{i=1}^{n-1}A_i\le 10^6,A_n\le 10^9\)

solution

smljwy...

显然当 \(A_n>\sum_{i=1}^{n-1}A_i\) 时已经做完了。

\(f_i\) 表示先手在 \(i-1\),后手在 \(i\) 时的答案,则

\[f_i=\max_{j=i+1}^n\{a_j+sum_{j-1}-f_j\}-sum_i \]

然后比较一下 \(f_i\)\(f_{i+1}\) 就会发现 \(f_i=|f_{i+1}-a_{i+1}|\)

考虑处理询问...

神仙的遗物

Q

题目描述:给定 \(n\) 个正实数 \(r_i\),构造 \(n\) 个点 \(P_i\) 使得 \(|OP_i|=r_i\) 且这 \(n\) 个点构成的凸包面积最大。

数据范围:\(3\le n\le 8\)

solution

枚举凸包然后上 Lagrange 乘数法即可。

不需要考虑不是凸包的情况,因为不可能最优。

时间复杂度 \(O(n!\log V)\)

#include<bits/stdc++.h>
using namespace std;
typedef double LD;
typedef long long LL;
const int N = 10;
const LD PI = acos(-1), eps = 3e-11;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, r[N], p[N], m; bool vis[N]; LD ans, res[N], tt[N];
void work(){
    LD R = 1e8; p[m] = p[0];
    for(int i = 0;i < m;++ i) chmin(R, (LD)r[p[i]] * r[p[i+1]]);
    LD L = -R, are = 0, mid, tmp;
    while(R - L > eps){
        mid = (L + R) / 2; tmp = 0;
        for(int i = 0;i < m;++ i) tmp += tt[i] = acos(mid / (r[p[i]] * r[p[i+1]]));
        tmp -= 2*PI; if(tmp > eps) L = mid; else if(tmp < -eps) R = mid; else break;
    } for(int i = 0;i < m;++ i) are += sqrt((LL)r[p[i]]*r[p[i]]*r[p[i+1]]*r[p[i+1]] - mid*mid);
    for(int i = 0;i < m;++ i) if(tt[i] < 2e-8) return;
    if(chmax(ans, are)){
        memset(res, 0, sizeof res);
        for(int i = 1;i < m;++ i){res[p[i]] = tt[i-1]; tt[i] += tt[i-1];}
    }
}
void dfs(int d){
    if(d == n) return; dfs(d+1);
    for(int i = m?p[0]+1:0;i < n;++ i) if(!vis[i]){
        p[m++] = i; vis[i] = true;
        if(m >= 3) work(); dfs(d+1);
        -- m; vis[i] = false;
    }
}
int main(){
    read(n); for(int i = 0;i < n;++ i) read(r[i]);
    dfs(0); for(int i = 0;i < n;++ i) printf("%.12lf\n", res[i]);
}

O

题目描述:给定质数 \(p\)\(n\) 次询问正整数 \(a,b,c,d\)

\[\min\{ax+by\mid x,y\in\N_+\and p|(c^x-d^y)\} \]

数据范围:\(n\le 10^4,a,b,c,d,p\le 10^9\)

solution

先求出原根 \(g\),设 \(c:=\ln_g c,d:=\ln_gd,p:=p-1\),条件改为 \(cx\equiv dy\pmod p\)。然后是常规操作:

\(q=\gcd(c,d,p)\),则 \(c:=\frac cq,d:=\frac dq,p:=\frac pq\)

\(q=\gcd(c,p)\),则 \(q|y\),则 \(c:=\frac cq,p:=\frac pq,b:=bq\),然后再令 \(d:=dc^{-1}\),条件改为 \(x\equiv dy\pmod p\)

\(q=\gcd(d,p)\),则 \(q|x\),则 \(d:=\frac dq,p:=\frac pq,a:=aq\)

条件改为 \(x\equiv dy\pmod p\),其中 \(d\bot p\)。首先特判 \(y=p\),然后 \(y\in[1,p)\),答案即为

\[ax+by=a(dy\bmod p)+by=(ad+b)y-ap\lfloor\frac{dy}p\rfloor \]

然后就可以上类欧了,设 \(f(y)=\lfloor\frac{cy+d}e\rfloor,F(l,r,a,b,c,d,e)=\min\{ay+bf(y)\mid y\in[l,r]\}\),其中 \(c,e>0\)

  • \(a=0\or b=0\or f(l)=f(r)\),则最小值当 \(y=l\)\(r\) 时取到。
  • \(c\notin[0,e)\),则 \(F(l,r,a,b,c,d,e)=F(l,r,a+b\lfloor\frac ce\rfloor,b,c\bmod e,d,e)\)
  • \(d\notin[0,e)\),则 \(F(l,r,a,b,c,d,e)=F(l,r,a,b,c,d\bmod e,e)+b\lfloor\frac de\rfloor\)
  • \(c<e,a>0\),设 \(L=f(l),R=f(r)\),则对于每个 \(k\in[L,R]\),当 \(y=\min\{y|f(y)=k\}\) 时最优。首先特判 \(k=L,y=l\),然后 \(y=\lfloor\frac{ke-d+c-1}c\rfloor\),所以 \(F(l,r,a,b,c,d,e)=\min\{al+bf(l),F(f(l)+1,f(r),b,a,e,c-d-1,c)\}\)
  • \(c<e,a<0\),设 \(L=f(l),R=f(r)\),则对于每个 \(k\in[L,R]\),当 \(y=\max\{y|f(y)=k\}\) 时最优。首先特判 \(k=R,y=r\),然后 \(y=\lfloor\frac{ke+e-d-1}c\rfloor\),所以 \(F(l,r,a,b,c,d,e)=\min\{ar+bf(r),F(f(l),f(r)-1,b,a,e,e-d-1,c)\}\)

时间复杂度 \(O(\sqrt{np}+n\log p+p^{\frac 14+\varepsilon})\)。注意多组询问的 BSGS好像上周就考过

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef __int128 LLL;
const int N = 4000003;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int cnt, head[N], to[N], val[N], nxt[N];
void add(int a, int c){
    to[++cnt] = a; nxt[cnt] = head[a%N]; head[a%N] = cnt; val[cnt] = c;
}
int find(int a){
    for(int i = head[a%N];i;i = nxt[i])
        if(to[i] == a) return val[i];
    return -1;
} int T, p, g, Step, pri[10], tot;
int ksm(int a, int b){
    int res = 1;
    for(;b;b >>= 1, a = (LL)a * a % p)
        if(b & 1) res = (LL)res * a % p;
    return res;
}
void work(int x){
    for(int i = 2;i * i <= x;++ i) if(!(x % i)){
        pri[tot++] = i; do x /= i; while(!(x % i));
    } if(x > 1) pri[tot++] = x;
}
int bsgs(int x){
    for(int i = 1;i <= Step;++ i){
        x = (LL)x * g % p;
        int tmp = find(x);
        if(tmp > 0) return (tmp * Step - i) % (p - 1);
    } return -1;
}
int c, d; LL a, b;
int gcd(int a, int b){return b ? gcd(b, a % b) : a;}
void exgcd(int a, int b, int &x, int &y){
    if(!b){x = 1; y = 0; return;}
    exgcd(b, a%b, y, x); y -= a / b * x;
}
LL F(LL l, LL r, LL a, LL b, LL c, LL d, LL e){
    if(l > r) return 1e18;
    if(c < 0){LL tt = (e-c-1) / e; a -= b * tt; c += e * tt;}
    if(c >= e){LL tt = c / e; a += b * tt; c -= e * tt;}
    LL ans = 0;
    if(d < 0){LL tt = (e-d-1) / e; ans -= tt; d += e * tt;}
    if(d >= e){LL tt = d / e; ans += tt; d -= e * tt;}
    LL L = (c * l + d) / e, R = (c * r + d) / e;
    if(!a || !b || L == R) return min(a*l + b*L, a*r + b*R);
    if(a > 0) return ans + min(a*l + b*L, F(L+1, R, b, a, e, c-d-1, c));
    return ans + min(a*r + b*R, F(L, R-1, b, a, e, e-d-1, c));
}
LL solve(){
    read(a); read(b); read(c); read(d); c %= p; d %= p;
    if(!c && !d) return a+b; if(!c || !d) return -1;
    c = bsgs(c); d = bsgs(d);
    int e = p-1, q = gcd(gcd(c, d), e);
    c /= q; d /= q; e /= q;
    q = gcd(c, e); c /= q; e /= q; b *= q;
    int x, y; exgcd(c, e, x, y);
    d = ((LL)d * x % e + e) % e;
    q = gcd(d, e); d /= q; e /= q; a *= q;
    return min((a + b) * e, F(1, e-1, a*d+b, -a*e, d, 0, e));
}
int main(){
    read(T); read(p); work(p-1); Step = sqrt(p / T) + 1;
    for(g = 2;;++ g){
        bool flg = true;
        for(int i = 0;i < tot && flg;++ i)
            if(ksm(g, (p-1)/pri[i]) == 1) flg = false;
        if(flg) break;
    } int tt = ksm(g, Step), lim = (p + Step - 1) / Step;
    for(int i = 1, v = tt;i <= lim;++ i, v = (LL)v * tt % p) add(v, i);
    for(int _ = 1;_ <= T;++ _) printf("%lld\n", solve());
}

U

题目描述:给定自然数序列 \(\{a\}_{i=0}^n\)\(\forall m\in[0,n]\cap\N\)

\[f_m=\sum_{i=0}^na_i\sum_{j=0}^n(-1)^j\binom mj\binom{n-m}{i-j}\bmod 998244353 \]

数据范围:\(n\le 10^6\)

solution

\(A(x)=\sum_{k\ge 0}a_kx^k\)\(F(x)=\sum_{k\ge 0}f_kx^k\)。设 \(\dot\times\) 是减法卷积,则

\[\begin{aligned} f_m&=((1-x)^m\times (1+x)^{n-m})\cdot A \\ &=((\frac{1-x}{1+x})^m\times(1+x)^n)\cdot A \\ &=(\frac{1-x}{1+x})^m\cdot(A\dot\times(1+x)^n) \\ &=(\sum_{k=0}^m(-2)^k\binom mk(\frac x{1+x})^k)\cdot(A\dot\times(1+x)^n) \\ &=\sum_{k=0}^m(-2)^k\binom mk\left((\frac x{1+x})^k\cdot(A\dot\times(1+x)^n)\right) \end{aligned} \]

\(P(x)=(A\dot\times(1+x)^n)\),则当 \(k>0\) 时右边的括号是

\[\sum_{l\ge k}\binom{-k}{l-k}[x^l]P(x)=\frac 1{(k-1)!}\sum_{l\ge k}\frac{(-1)^{l-k}}{(l-k)!}\cdot(l-1)![x^l]P(x) \]

也是减法卷积。然后算 \(f\) 是一个正常卷积(雾),时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1<<21, mod = 998244353;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int ksm(int a, int b){
    int res = 1;
    for(;b;b >>= 1, a = (LL)a * a % mod)
        if(b & 1) res = (LL)res * a % mod;
    return res;
}
int n, A[N], B[N], fac[N], inv[N], lim, rev[N], w[2][N], ans;
void init(int n){
    fac[0] = 1;
    for(int i = 1;i <= n;++ i) fac[i] = (LL)fac[i-1] * i % mod;
    inv[n] = ksm(fac[n], mod-2);
    for(int i = n;i;-- i) inv[i-1] = (LL)inv[i] * i % mod;
}
int C(int n, int m){return (LL)fac[n] * inv[m] % mod * inv[n-m] % mod;}
void qmo(int &x){x += x >> 31 & mod;}
void calrev(int len){
    int L = -1; lim = 1;
    while(lim <= len){lim <<= 1; ++ L;}
    for(int i = 0;i < lim;++ i) rev[i] = (rev[i>>1]>>1) | ((i&1)<<L);
    for(int mid = 1;mid < lim;mid <<= 1){
        w[0][mid] = w[1][mid] = 1;
        int Wn = ksm(3, (mod-1)/(mid<<1));
        for(int i = 1;i < mid;++ i) w[0][mid+i] = (LL)w[0][mid+i-1] * Wn % mod;
        for(int i = 1;i < mid;++ i) w[1][mid+i] = mod - w[0][(mid<<1)-i];
    }
}
void NTT(int *A, int op){
    for(int i = 0;i < lim;++ i)
        if(i < rev[i]) swap(A[i], A[rev[i]]);
    for(int mid = 1;mid < lim;mid <<= 1)
        for(int i = 0;i < lim;i += mid<<1)
            for(int j = 0;j < mid;++ j){
                int y = (LL)A[i+j+mid] * w[op][mid+j] % mod;
                qmo(A[i+j+mid] = A[i+j] - y); qmo(A[i+j] += y - mod);
            }
    if(op){
        int inv = ksm(lim, mod-2);
        for(int i = 0;i < lim;++ i) A[i] = (LL)A[i] * inv % mod;
    }
}
int main(){
    read(n); init(n); calrev(n<<1);
    for(int i = 0;i <= n;++ i) read(A[i]);
    for(int i = 0;i <= n;++ i) B[i] = C(n,i);
    NTT(A, 0); NTT(B, 0);
    for(int i = 0;i < lim;++ i) B[i] = (LL)A[i] * B[i] % mod;
    NTT(B, 1); int tmp = B[n]; A[0] = 0;
    for(int i = 1;i <= n;++ i) A[i] = (LL)B[i+n] * fac[i-1] % mod;
    for(int i = 0;i <= n;++ i) B[n-i] = i&1 ? mod-inv[i] : inv[i];
    for(int i = n+1;i < lim;++ i) A[i] = B[i] = 0;
    NTT(A, 0); NTT(B, 0);
    for(int i = 0;i < lim;++ i) B[i] = (LL)A[i] * B[i] % mod;
    NTT(B, 1); A[0] = tmp;
    for(int i = 1, pw = 1;i <= n;++ i){
        qmo(pw = mod - 2 * pw);
        A[i] = (LL)B[i+n] * inv[i-1] % mod * pw % mod * inv[i] % mod;
    } for(int i = 0;i <= n;++ i) B[i] = inv[i];
    for(int i = n+1;i < lim;++ i) A[i] = B[i] = 0;
    NTT(A, 0); NTT(B, 0);
    for(int i = 0;i < lim;++ i) A[i] = (LL)A[i] * B[i] % mod;
    NTT(A, 1);
    for(int i = 0;i <= n;++ i) ans ^= (LL)A[i] * fac[i] % mod;
    printf("%d\n", ans);
}

-PE729 Range of periodic sequence

题目描述:定义形如 \(a_{n+1}=a_n-\frac1{a_n}\) 的实周期序列 \(\{a\}_{n\ge 0}\)好序列\(S(P)\) 是所有周期 \(\le P\) 的好序列的极差之和。求 \(S(25)\)

*PE676 Matching Digit Sums

题目描述:设 \(d(k,b)\)\(k\)\(b\) 进制下的数字和,\(M(n,b_1,b_2)=\sum_{k\le n}k[d(k,b_1)=d(k,b_2)]\),求 \(\sum_{k=3}^6\sum_{l=1}^{k-2}M(10^{16},2^k,2^l)\)

solution

直接对 \(2^{\text{lcm}(k,l)}\) 进制上搞数位 dp 即可。

*PE664 An infinite game

solution

这是一个叫 Conway Soldier 的经典问题。

有一个想法,对每个棋子设一个能量,每走一步就要将能量提升至原来的至少 \(t\) 倍,其中 \(t^2=1+t\) 表示通过另一枚棋子移动到更远的地方。

显然能量只会散失不会增多,上界就是整个棋盘所能提供的能量之和能走到的地方,且这是取不到的,因为只能走有限步,不可能把能量全部用完(只剩一个棋子)。

然而不知道为什么答案就是这个上界我飞升了

\[\begin{aligned} \phi&=\frac{\sqrt 5-1}2 \\ \Phi&=\sum_{i\ge0}\sum_j\phi^{i+|j|}(i+1)^n \\ &=\frac{7+3\sqrt 5}2\sum_{i\ge 1}\phi^ii^n \\ Ans&=\lfloor-\frac{\log\Phi}{\log\phi}\rfloor \end{aligned} \]

考虑后面那一坨,有一个东西叫多重对数函数

\[\text{Li}_s(z)=\sum_{k\ge 1}\frac{z^k}{k^s} \]

通常定义在 \(|z|<1\)\(s\in\C\)。此时所求即为 \(\text{Li}_{-n}(\phi)\)。这东西有一些性质:

\[\text{Li}_{s+1}(z)=\int_0^z\frac{\text{Li}_s(t)}t\text dt \\ \text{Li}_{-n}(z)=\left(z\frac{\text d}{\text dz}\right)^n\frac{z}{1-z}=\sum_{k=0}^nk!S(n+1,k+1)\left(\frac z{1-z}\right)^{k+1} \\ \text{Li}_{-n}(z)=\frac{\sum_{k=0}^{n-1}A(n,k)z^{n-k}}{(1-z)^{n+1}} \\ \]

其中分子被称为欧拉多项式\(A(n,k)\)欧拉数

\[\text{Li}_s(e^{\mu})=\Gamma(1-s)(-\mu)^{s-1}+\sum_{k\ge 0}\frac{\zeta(s-k)}{k!}\mu^k \]

\(\text{Li}_s(e^{\mu})\) 的幂级数表示原因完全不懂,所以

\[\text{Li}_s(z)\approx\Gamma(1-s)(-\ln z)^{s-1}\pod{s\ll 0} \]

直接往里代就可以把这题做完...误差貌似很小的样子...

这个函数背景丰富得很,学不来学不来...

*PE665 Proportionate Nim

题目描述:给定自然数二元组 \((x,y)\),操作可以将其变为 \((x-n,y),(x,y-n),(x-n,y-n),(x-2n,y-n),(x-n,y-2n)\)。两人轮流操作,求 \(x+y\le M,x\le y\) 的必败点的 \(x+y\) 之和。

数据范围:\(M=10^7\)

solution

打表会发现 \(x/y\approx1.477,2.247\)

已知结论时,可以通过对整点个数做算两次来计算出这两个常数,具体来说是必败点的 \(x,y,x-y,x-2y,2x-y\) 分别不同,覆盖对应直线上的整点...

然而具体过程看不懂,自闭了。改改枚举顺序就线性了,我飞升。

*PE631 Constrained Permutations

题目描述:求长度 \(\le 10^{18}\) 的排列数量\(\bmod(10^9+7)\),使得其不存在大小关系为 \((1,2,4,3)\) 的子序列,且逆序对个数 \(\le 40\)

solution

首先考虑暴力,将排列变换一下:\(P'[i]=n+1-P[n+1-i]\),则限制改为 \((2,1,3,4)\)

然后从前往后 dp,仅考虑相对大小,插一个数到第 \(i\) 位。设 \(f_{i,j,k,l}\) 表示长为 \(i\) 的排列,\((2,1)\) 中最小的 \(2\)\(j\)\((2,1,3)\) 中最小的 \(3\)\(k\),逆序对个数是 \(l\)。直接枚举下一位转移,时间复杂度 \(O(n^3m^2)\)

然后发现当 \(m=40,n\ge 42\) 时,答案都是 \(53393415\)。实际上可以证明一个结论:

\(n>m+\sqrt{2m}\) 时,长为 \(n+1\) 的合法排列由长为 \(n\) 的合法排列 \(+(n+1)\) 构成。

证明:若长为 \(n+1\) 的合法排列末尾不是 \(n+1\),设 \(p_c=n+1\),则 \(c>n-m\)。前 \(\sqrt{2m}\) 个数里必定有 \(a<b,p_a<p_b\)。这时 \(a<b<c<n+1\)\(p_a<p_b<p_{n+1}<p_c\),矛盾,Q.E.D.

AGC048E Strange Relation

solution

首先考虑给定 \(A,T\) 时怎么做。设 \(z_i=A_i+Tx_i\),易得 \(z_1=A_1\)。对于答案 \(x\)\(\forall i>1,z_i\notin(A_1-T,A_1]\),因为如果存在这样的 \(i\),把其中最左的 \(x_i:=x_i+1\) 后仍然满足条件。

考虑去除第一位,设 \(x'_i=x_{i+1}-[A_1<z_{i+1}]\),则 \(x'\)\((A_2,\dots,A_n)\) 满足条件,还能发现操作可逆,且 \(x=f(A,T)\Leftrightarrow x'=f((A_2,\dots,A_n),T)\)

然后就可以 dp 了,设 \(g(A,T)\) 表示 \(f(A,T)\) 中最末元素的值,则 \(g(A,T)\) 只与 \(g((A_2,\dots,A_n),T),A_1,A_n\) 有关。枚举 \(A_n\),设 \(dp_{i,j}\) 表示 \((A_i,\dots,A_n)\) 的数量使得 \(g((A_i,\dots,A_n),T)=j\)

其他位置同理,时间复杂度 \(O(n^3k)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 53, mod = 1e9 + 7;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, m, T, dp[N][N], pw[N];
vector<int> b[N];
int main(){
    read(n); read(m); read(T);
    for(int i = 1;i <= n;++ i){
        b[i].resize(m);
        for(int j = 0;j < m;++ j) read(b[i][j]);
        sort(b[i].begin(), b[i].end());
    } pw[0] = 1;
    for(int i = 1;i <= n;++ i) pw[i] = (LL)pw[i-1] * m % mod; puts("0");
    for(int i = 2;i <= n;++ i){
        int ans = 0;
        for(int x : b[i]){
            memset(dp, 0, sizeof dp); dp[i][0] = 1;
            for(int j = i;j > 1;-- j){
                int pos = 0;
                for(int l = 0;l <= i-j;++ l) if(dp[j][l]){
                    int val = dp[j][l], tmp = x + (l+1) * T;
                    while(pos < m && b[j-1][pos] < tmp) ++ pos;
                    dp[j-1][l] = (dp[j-1][l] + (LL)(m-pos) * val) % mod;
                    dp[j-1][l+1] = (dp[j-1][l+1] + (LL)pos * val) % mod;
                }
            } for(int j = 1;j < i;++ j) ans = (ans + (LL)dp[1][j] * j) % mod;
        } printf("%lld\n", (LL)ans * pw[n-i] % mod);
    }
}

*AGC037E Reversing and Concatenating

题目描述:给定长为 \(n\) 的字符串 \(S\),一次操作把 \(S\) 变成 \(SS^R\) 的一个子串。求 \(k\) 次操作之后可能得到的字典序最小的字符串。

数据范围:\(n\le 5000,k\le 10^9,|\Sigma|=26\)

solution

设字符串中最多有连续 \(c\) 个最小字符,发现答案的开头一定是 \(c\cdot 2^{k-1}\) 个最小字符。

然后剩下部分就确定好了。直接暴力对比每种方案,随便 \(O(n^2)\),优化一下大概可以更快。

AGC037F Counting of Subarrays

题目描述:定义正整数序列 \(S\)\((k,l)\)-good 的(\(k,l\in\N_+\))当且仅当 \(S=(k)\) 或存在 \(S\) 的一个划分 \(T_1,T_2,\dots,T_m(m\ge l)\),使得 \(T_i\)\((k-1,l)\)-good 的。

给定长为 \(n\) 的正整数序列 \(S\) 和正整数 \(L\),求 \(S\) 有多少个子串满足 \(\exist K\in\N_+\),使得 \(S\)\((K,L)\)-good 的。

数据范围:\(n\le 2\cdot10^5,2\le L\le n,1\le S_i\le 10^9\)

solution

看上去没有想象中nan...可是还是不会做

首先有一个判断合法的暴力:

  • 若这个序列长为 \(1\),则它必定合法。
  • 设这个序列的最小值是 \(m\),极长区间是 \([l_1,r_1],\dots,[l_k,r_k]\),若有一个区间长度 \(<L\) 则不合法,否则可以将其压缩成 \(\lfloor\frac{r_i-l_i+1}L\rfloor\)\(m+1\)(易得丢数不会更优),然后递归下去。

虽然看上去很暴力,但因为每访问 \(L\) 个元素就去掉 \(L-1\) 个,若使用 set/priority_queue 等维护,时间复杂度是 \(O(n\log n)\) 的。

但实际上可以用一个类似维护单调栈的方法。维持栈元素单调不升,每次加进一个元素时,若它比栈顶元素大则其不可能与前面的数合并,直接合并掉栈顶的一堆数即可。最后剩下的可以直接一步一步合并,时间复杂度 \(O(n)\)

然后考虑计数。由于合并之后可能会有一个位置代表多个初始位置,所以要带上些系数。设 \(L_k,R_k\) 分别表示第 \(k\) 个位置作为左端点和右端点时的系数,则

  • 同理设出 \(m,[l_i,r_i]\),每个区间的贡献是 \(\sum L_iR_j[i=j\or j-i+1\ge L]\)
  • 然后将其压缩成 \(\lfloor\frac{r_i-l_i+1}L\rfloor\)\(m+1\)\(L_i,R_i\) 的值需要手玩一下,保持先后贡献相同。题解的栗子是当 \(L=3,10\)\(1\) 压缩成 \(3\)\(3\) 时,\(L_i\) 变为 \(L_1+L_2,L_3+L_4+L_5,L_6+L_7+L_8\)\(R_i\) 变为 \(R_3+R_4+R_5,R_6+R_7+R_8,R_9+R_{10}\)
  • 最后递归下去。为了避免重复计数,需要再去掉当前区间的贡献。

然后直接用同样的方法,时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200003;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, L, top, leaf[N], rigt[N], t1[N], t2[N], val[N]; LL ans;
void work(){
	int now = top;
	while(val[now] == val[now-1]) -- now;
	int d = top-now+1, v = val[now]; LL tmp = 0;
	for(int i = now;i <= top;++ i){
		if(i-L+1 >= now) tmp += leaf[i-L+1];
		ans += (tmp+leaf[i]) * rigt[i];
	}
	if(d < L){top = now-1; while(top) work(); return;}
	int nd = d / L;
	memset(t1, 0, nd+1<<2); memset(t2, 0, nd+1<<2);
	for(int i = now;i <= top;++ i){
		t1[(top-i+1)/L] += leaf[i];
		t2[(i-now+1)/L] += rigt[i];
	} top = now+nd-1;
	for(int i = 1;i <= nd;++ i){
		leaf[top-i+1] = t1[i];
		rigt[now+i-1] = t2[i];
	} tmp = 0;
	for(int i = now;i <= top;++ i){
		val[i] = v+1;
		if(i-L+1 >= now) tmp += leaf[i-L+1];
		ans -= (tmp+leaf[i]) * rigt[i];
	}
}
int main(){
	read(n); read(L);
	for(int i = 1, x;i <= n;++ i){
		read(x);
		while(top && val[top] < x) work();
		++top; leaf[top] = rigt[top] = 1; val[top] = x;
	} while(top) work();
	printf("%lld\n", ans);
}

*PE627 Counting Products

题目描述:设 \(F(S,n)=\{\prod_{i=1}^n x_i\mid x_i\in S\}\),求 \(F([1,m]\cap\N,n)\bmod(10^9+7)\)

数据范围:\(m=30,n=10001\)

solution

首先是正常一点的做法:猜它是个 \(\pi(m)\) 次多项式,然后暴力+插值...吗?发现暴力不太能很快跑出来的样子...

然而可以优化一下,对于质数 \(p\),若 \(p\nmid x,\forall x\in S\),则 \(F(S\cup\{p\},n)=\sum_{k=0}^nF(S,k)\)。于是只需插值到 \(n=\pi(\frac m2)=6\) 即可。

然后是数理基础(?)做法:有个东西叫Ehrhart polynomial

对于 \(d\) 维空间中的整点多面体 \(P\),设 \(\mathcal L\) 是与 \(\Z^d\) 同构的加法群,\(L(P,t)=\#(tP\cap\mathcal L)\),则存在 \(L_d(P),L_{d-1}(P),\dots,L_0(P)\in\Q\) 使得 \(L(P,t)=\sum_{k=0}^dL_k(P)t^k,\forall t\in\N_+\)。设 \(\text{int}(P)\)\(P\) 的内部点构成的点集,则 \(L(\text{int}(P),t)=(-1)^dL(P,-t)\)

然后就可以做这个题了,设 \(k=\pi(m)\),则 \(S=\{1,2,\dots,m\}\) 可以看做 \(k\) 维空间中的点,设 \(\bar{S}\) 是这些点构成的凸包,则 \(F(S,n)=L(\bar S,n)\)\(n\)\(k\) 次多项式。设 \(k'=\pi(\sqrt m)\),则 \(\forall t\in[1,k-k'-1],(-1)^dL(\bar S,-t)=L(\text{int}(\bar S),t)=0\),所以只需插值到 \(n=k'+1=4\) 即可。

\[F([1,30]\cap\N,n)=\frac1{10!}(n+1)^{7\uparrow}(59 n^3+563 n^2+1358 n+720) \]

稍微拓展一下,还有一个东西叫 The Betke-Kneser theorem:

定义在整点多边形上的函数 \(Z\)\(\text{SL}(d,\Z)\),且是平移不变的valuation当且仅当存在实数 \(c_0,\dots,c_n\) 使得 \(Z=\sum_{k=0}^dc_kL_k\)

看不懂条件是啥意思,但至少通常所说的"体积"满足这个条件,于是这个可以看做高维情况的Pick's theorem

*PE556

题目描述:求范数 \(\le 10^{14}\) 的免平方高斯整数的个数。

可以看看 2019 年的论文学习下高斯整数的科技两篇撞主题可还行

跟整数做法完全一样iee

*PE558

题目描述:设 \(r\) 满足 \(r^3=r^2+1\),对于 \(n\in\N_+\),若 \(n=\sum_kb_kr^k\),其中:

  • \(b_k\in\{0,1\},\forall k\in\Z\)
  • \(b_k+b_{k+1}+b_{k+2}\le 1,\forall k\in\Z\)
  • \(w(n)=\sum_k b_k\) 有限。

(1) 证明:这样的 \(\{b_k\}\) 是唯一的。 (2) 求 \(\sum_{i=1}^{5\cdot10^6}w(i^2)\)

solution

题解里面有 4 种做法我都不会

首先假设解一定存在

法一:找到一个支持高精小数的语言,然后上贪心...

法二:考虑贪心。要进行的操作即为对于多项式 \(P(x)\),计算 \(P(r)\) 的符号。考虑矩阵

\[A=\begin{bmatrix}1 & 1 & 0 \\ 0 & 0 & 1 \\ 1 & 0 & 0\end{bmatrix} \]

它的最小多项式是 \(x^3-x^2-1\),设另外两个复根是 \(z\)\(\overline z\),所以 \(\det(P(A))=P(r)P(z)P(\overline z)=P(r)P(z)\overline{P(z)}\),后面两个数之积一定 \(>0\),所以 \(\text{sgn}(P(r))=\text{sgn}(\det(P(A)))\),代入矩阵即可,需要高精整数板子。

法三:考虑扩张域,\(r\)\(\Q\) 中的极小多项式是 \(x^3-x^2-1\),且因为 \(P(r)=\sum_kb_kr^k-n=0\),所以 \((x^3-x^2-1)|(\sum_kb_kx^k-n)\),则 \(P(r)=P(z)=P(\overline z)=0\),所以 \(P(r)+P(z)+P(\overline z)=\sum_kb_k(r^k+z^k+\overline z^k)-3n=0\),设 \(a_k=r^k+z^k+\overline z^k\),则 \(a_0=3,a_1=a_2=1,a_n=a_{n-1}+a_{n-3}\),所以 \(\sum_ka_kb_k=3n\)

然后就可以证明解唯一,使用类似 Fibonacci 表示法的贪心方法即可证明。所以贪心即可,需要高精整数板子。

法四:考虑操作,构造以下 \(5\) 个方程:

\[\begin{cases} 1+r^2&=r^3 \\ 1+r^{-2}&=r \\ 1+r&=r^2+r^{-3} \\ 1+r^{-1}&=r+r^{-4} \\ 1+1&=r+r^{-2}+r^{-7} \end{cases} \]

假设现在已经求出了 \(k\) 的表示,要求 \(k+r^m\) 的表示。dfs 判断当前位置左右共 \(5\) 个位置是否有值,如果有则使用这几个方程分散地递归下去。

鬼知道为什么 dfs 不仅能停下还跑得挺快。

## -PE579

题目描述:设 \(S(n)\) 表示所有坐标在 \([0,n]\) 的格点立方体内部的点的个数之和。求 \(S(5000)\bmod(10^9+7)\)

-WTF19 E e

题目描述:给定正整数 \(n,m\) 和长为 \(n\) 的字符串 \(S\),在男厕所中有 \(m\) 个坑位,充分多的人按顺序上厕所,每个人占到坑位之后就会一直占着不走。定义一个位置是舒服的(?)当且仅当这个位置没有人,且旁边两个位置都没有人。每个人来到厕所时会随机选一个舒服的位置占着,如果没有舒服的位置就会憋着。在占完位置之后,rng_58 会随机选择连续 \(n\) 个位置,记录下每个坑位是否有人,求在 \(m\rightarrow+\infty\) 时得到字符串 \(S\) 的概率(X 表示有人,. 表示没人)。已知答案必定是 \(r+\frac pe+\frac q{e^2}\),其中 \(r,p,q\in\mathbb Q\),求 \(r,p,q\) 模大质数的值。

数据范围:\(n\le 1000\)

solution

传说中 atc 的最难题目,所以我不会

发现样例:\(n=1,S=\) X 时答案是 \(\frac 12-\frac 1{2e^2}\),这个求的实际上就是厕所坑位的利用率。考虑毕导做法,设 \(f_m\) 表示 \(m\) 个坑位时的利用率,枚举第一个人占哪儿,显然(?)两边分割后独立,递归考虑,得到一个齐次线性递推,求 \(\lim\limits_{m\rightarrow+\infty}\frac{f_m}m\),只要有数理基础就可以解出来,于是就浪费了我们和hz学长一下午时间

考虑题解做法,转化一下题面:给每个坑位赋一个随机权值 \(x_i\in[0,1]\),每个人进来之后选择权值最小的舒服的位置。然后观察一下一个坑位何时会被占。设 \(x_{l-1}<x_l>x_{l+1}>\dots>x_i<\dots<x_{r-1}<x_r>x_{r+1}\),则手玩一下就能发现 \(i\) 被占当且仅当 \(2|(i-l)\and 2|(r-i)\)

两边显然独立,于是只考虑一边极长下降后缀的长度为偶数的概率(设 \(x=x_i\)):

\[\begin{aligned} &\sum_{j\ge 0}[2|j]\Pr(x_{i-j-1}<x_{i-j}>x_{i-j+1}>\dots>x_i) \\ =&\sum_{j\ge 0}(-1)^j\Pr(x_{i-j}>x_{i-j-1}>\dots>x_i) \\ =&\sum_{j\ge 0}\frac{(-1)^j}{j!}=e^{-x} \end{aligned} \]

两边都满足的概率即为 \(e^{-2x}\),取积分即为 \(\int_0^1e^{-2x}\text dx=\frac 12-\frac1{2e^2}\)

这个做法一看就很能扩展rng_58 wsm这么强啊,考虑上述的这个过程,每个极小值肯定是 X,且每个 .. 左右两边必定是先升后降。于是可以对 \(S\) 从每个 .. 的中线割开,每个部分独立。共有两种情况:两个半开半闭+一堆闭合的,或者一个全开的。

  1. 对于一个闭合的段,等价于对于长为 \(2n+1\) 的排列,要求极小值都是偶数位,求概率。直接 \(O(n^2)\) dp。
  2. 对于一个半开的段(不妨假设左开右闭),枚举最靠右的极大值的位置,然后递归处理。
  3. 对于一个全开的段,#!@#@)(&#!@!()...不会做

AtCoder Petrozavodsk Contest 001

I Simple APSP Problem

题目描述:给定 \(W\times H\) 的网格图,边权都为 \(1\),删掉 \(N\) 个点之后求两两距离之和\(\bmod(10^9+7)\)

数据范围:\(W,H\le 10^6,N\le 30\)

solution

拿头做...

对于完整的两行,计算上跨过两行的贡献之后就可以把这两行合并。列同理。

最后只剩下 \(2N\times 2N\) 的网格图,直接暴力 bfs,时间复杂度 \(O(N^4)\)

#include<bits/stdc++.h>
#define fi first
#define se second
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1000003, mod = 1e9 + 7, d[2][4] = {{0, 1, 0, -1}, {1, 0, -1, 0}};
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int w, h, n, x[33], y[33], sx[N], sy[N], idx[N], idy[N], lenx[66], leny[66], totx, toty, ans, dis[66][66], f, r;
bool vis[66][66]; pii q[4000];
void bfs(int x, int y){
    memset(dis, 0x3f, sizeof dis);
    q[0] = MP(x, y); dis[x][y] = f = 0; r = 1;
    while(f < r){
        int x = q[f].fi, y = q[f].se; ++ f;
        for(int i = 0;i < 4;++ i){
            int nx = x + d[0][i], ny = y + d[1][i];
            if(nx > 0 && nx <= totx && ny > 0 && ny <= toty && !vis[nx][ny] && chmin(dis[nx][ny], dis[x][y] + 1))
                q[r++] = MP(nx, ny);
        }
    }
}
int main(){
    read(w); read(h); read(n);
    for(int i = 1;i <= n;++ i){
        read(x[i]); read(y[i]);
        ++sx[++x[i]] ; ++sy[++y[i]];
    } for(int i = 1;i <= w;++ i) sx[i] += sx[i-1];
    for(int i = 1;i <= h;++ i) sy[i] += sy[i-1];
    idx[1] = totx = 1;
    for(int i = 2;i <= w;++ i)
        if(sx[i] == sx[i-2]){
            idx[i] = idx[i-1];
            ans = (ans + ((i-1ll)*h-sx[i]) % mod * (((w-i+1ll)*h-n+sx[i]) % mod)) % mod;
        } else idx[i] = ++totx;
    idy[1] = toty = 1;
    for(int i = 2;i <= h;++ i)
        if(sy[i] == sy[i-2]){
            idy[i] = idy[i-1];
            ans = (ans + ((i-1ll)*w-sy[i]) % mod * (((h-i+1ll)*w-n+sy[i]) % mod)) % mod;
        } else idy[i] = ++toty;
    for(int i = 1, j = 1;i <= w;i = j){
        while(idx[i] == idx[j]) ++j; lenx[idx[i]] = j-i;
    } for(int i = 1, j = 1;i <= h;i = j){
        while(idy[i] == idy[j]) ++j; leny[idy[i]] = j-i;
    } for(int i = 1;i <= n;++ i) vis[idx[x[i]]][idy[y[i]]] = true;
    for(int i = 1;i <= totx;++ i)
        for(int j = 1;j <= toty;++ j) if(!vis[i][j]){
            bfs(i, j); int tmp = 0;
            for(int k = i;k <= totx;++ k)
                for(int l = (k==i?j+1:1);l <= toty;++ l) if(!vis[k][l])
                    tmp = (tmp + (LL)dis[k][l] * lenx[k] % mod * leny[l]) % mod;
            ans = (ans + (LL)tmp * lenx[i] % mod * leny[j]) % mod;
        }
    printf("%d\n", ans);
}

AGC031E Snuke the Phantom Thief

solution

好像是经典trick,可惜我不会

先考虑一维做法,将宝石的坐标排序,可以将条件转化:\(\le b_i\) 的宝石 \(\le a_i\)\(\Leftrightarrow\)\(a_i+1\) 个宝石的坐标 \(\ge b_i+1\)\(\ge b_i\) 的宝石 \(\le a_i\)\(\Leftrightarrow\)\(k-a_i\) 个宝石的坐标 \(\le b_i-1\)

设得到的限制是第 \(i\) 个宝石的坐标 \(\in[L_i,R_i]\),可以对 \(L\) 取前缀 \(\max\)\(R\) 取后缀 \(\min\),之后就不需要考虑坐标不降的条件了(显然不是不降的不会更优)

可以轻松扩展到二维,第 \(i\) 个宝石的坐标 \(x_i\in[LX_i,RX_i],y_i\in[LY_i,RY_i]\)

将每个宝石拆成出/入点,横/纵坐标分别建点,建图跑最大费用最大流,时间复杂度 \(O(可过)\)...

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 327, M = 33333;
const LL INF = 0x3f3f3f3f3f3f3f3f;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
void read(char &ch){
    do ch = getchar();
    while(ch != 'L' && ch != 'R' && ch != 'U' && ch != 'D');
}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, m, x[83], y[83], a[323], b[323]; char opt[323]; LL v[83], ans;
int cnt, S, T, head[N], to[M], nxt[M], cap[M], Lx[103], Rx[103], Ly[103], Ry[103]; LL cst[M];
void add(int a, int b, int c, LL d){
    to[++cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; cap[cnt] = c; cst[cnt] = d;
    to[++cnt] = a; nxt[cnt] = head[b]; head[b] = cnt; cap[cnt] = 0; cst[cnt] = -d;
}
LL dis[N]; bool inq[N];
int incf[N], pre[N], q[N], fr, re;
void AMOD(int &x){++ x; if(x == N) x = 0;}
bool spfa(){
    memset(dis, 0x3f, sizeof dis);
    memset(inq, 0, sizeof inq); fr = re = 0;
    q[re++] = S; incf[S] = 1e9; dis[S] = 0;
    while(fr != re){
        int u = q[fr]; AMOD(fr); inq[u] = false;
        for(int i = head[u];i;i = nxt[i])
            if(cap[i] > 0 && chmin(dis[to[i]], dis[u] + cst[i])){
                incf[to[i]] = min(incf[u], cap[i]); pre[to[i]] = i;
                if(!inq[to[i]]){q[re] = to[i]; AMOD(re); inq[to[i]] = true;}
            }
    } return dis[T] < INF;
}
void solve(int k){
    cnt = 1; memset(head, 0, sizeof head); S = N-1; T = S-1;
    for(int i = 1;i <= k;++ i){Lx[i] = Ly[i] = 1; Rx[i] = Ry[i] = 100;}
    for(int i = 1;i <= m;++ i)
        if(b[i] < k) switch(opt[i]){
            case 'L': chmax(Lx[b[i]+1], a[i]+1); break;
            case 'R': chmin(Rx[k-b[i]], a[i]-1); break;
            case 'D': chmax(Ly[b[i]+1], a[i]+1); break;
            case 'U': chmin(Ry[k-b[i]], a[i]-1);
        }
    for(int i = 2;i <= k;++ i){chmax(Lx[i], Lx[i-1]); chmax(Ly[i], Ly[i-1]);}
    for(int i = k-1;i;-- i){chmin(Rx[i], Rx[i+1]); chmin(Ry[i], Ry[i+1]);}
    for(int i = 1;i <= n;++ i) add(i+k, i+k+n, 1, -v[i]);
    //printf("k = %d\n", k);
    for(int i = 1;i <= k;++ i){
        //printf("Lx[%d] = %d, Rx[%d] = %d, Ly[%d] = %d, Ry[%d] = %d\n", i, Lx[i], i, Rx[i], i, Ly[i], i, Ry[i]);
        add(S, i, 1, 0); add(i+n+n+k, T, 1, 0);
        for(int j = 1;j <= n;++ j){
            if(x[j] >= Lx[i] && x[j] <= Rx[i]) add(i, j+k, 1, 0);
            if(y[j] >= Ly[i] && y[j] <= Ry[i]) add(j+k+n, i+n+n+k, 1, 0);
        }
    } int maxf = 0; LL res = 0;
    while(spfa()){
        int f = incf[T]; maxf += f; res += f * dis[T];
        for(int u = T;u != S;u = to[pre[u] ^ 1]){
            assert(cap[pre[u]] > 0);
            cap[pre[u]] -= f; cap[pre[u] ^ 1] += f;
        }
    } if(maxf == k) chmax(ans, -res);// printf("res = %lld\n", -res);
}
int main(){
    read(n);
    for(int i = 1;i <= n;++ i){
        read(x[i]); read(y[i]); read(v[i]);
    } read(m);
    for(int i = 1;i <= m;++ i){
        read(opt[i]); read(a[i]); read(b[i]);
    } for(int k = 1;k <= n;++ k) solve(k);
    printf("%lld\n", ans);
}

AGC046E Permutation Cover

题目描述:给定 \(k\) 个正整数 \(a_i\)。构造满足下列条件的字典序最小的序列 \(P\)(需判断无解):

  • \(P\) 中元素 \(\in[1,k]\)
  • 对于 \(i\in[1,k]\)\(P\) 中出现 \(i\) 恰好 \(a_i\) 次。
  • 对于 \(P\) 中任意一项,存在一个长为 \(k\) 的包含它的子串是排列。

数据范围:\(k\le 100,\sum a_i\le 10^3\)

solution

还是不会猜结论...

结论:有解的充要条件是 \(2\min\le\max\)

必要性:若 \(\exist x,y\) 使得 \(a_x>2a_y\),则在任意一个序列中必定存在 \(x\) 到左右两边最近的 \(x\)(或边界)之间没有 \(y\),这个 \(x\) 不满足条件 3,所以无解。

充分性:若 \(\forall x,y\)\(a_x\le 2a_y\),则对于任意一个 \(S\subseteq\{1,2,\dots,k\}\),必定存在一个满足条件的序列 \(P\),使其 \(S\) 中的元素出现两次,\(\overline S\) 中的元素出现一次。把这样的一堆序列拼起来一定可以满足要求,所以必定有解。

但题目要求字典序最小,因此需要贪心构造。设当前还需要放 \(b_i\)\(i\)

结论:确定自身满足条件的前缀的情况下,有解的充要条件是 \(2\min\le \max\)\(2\min+1=\max\) 且对于前缀的后 \(k\) 位组成的排列 \(Q\),满足所有 \(b_i=\min\) 的元素在 \(b_i=\max\) 的元素之前。证明同理(?)

每次加一个最小的满足条件的元素即可,时间复杂度 \(O(k\sum a_i)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 1003;
template<typename T>
void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int k, a[N], n, mx, mn, ans[N], pre[N], cnt[N]; bool ok[N];
int main(){
    read(k); mn = N;
    for(int i = 1;i <= k;++ i){
        read(a[i]); n += a[i]; chmax(mx, a[i]); chmin(mn, a[i]);
    } if(mx > (mn<<1)){puts("-1"); return 0;}
    for(int i = 1;i <= n;++ i){
        mx = 0; mn = N;
        for(int j = 1;j <= k;++ j){chmax(mx, a[j]); chmin(mn, a[j]);}
        int pos = N;
        for(int j = 1;j <= k;++ j) if(a[j] == mn) chmin(pos, pre[j]);
        for(int j = 1;j <= k;++ j)
            if(!ok[j] && (a[j]<<1) > mx && !(mx >= (mn<<1|1) && pre[j] > pos)){
                pre[j] = i; --a[j]; ok[j] = true; ++ cnt[j];
                printf("%d%c", ans[i] = j, " \n"[i==n]); break;
            } if(i > k) --cnt[ans[i-k]];
        bool flg = true;
        for(int j = 1;j <= k && flg;++ j) flg &= cnt[j];
        if(flg) memset(ok, 0, sizeof ok);
    }
}

*CF1477F Nezzar and Chocolate Bars

题目描述:给定正整数集 \(S\) 和正整数 \(K\),每次操作按值为权重随机选择数 \(x\),然后均匀随机选择 \(r\in(0,x)\),将 \(x\)\(S\) 中删去并加入 \(r\)\(x-r\)。求使得 \(\max S\le K\) 的期望操作次数\(\bmod 998244353\)

数据范围:\(|S|\le 50,K,\text{sum}(S)\le 2000\)

solution

纯翻译官方题解,我壬傻了

转化一下题面:有一条长为 \(\text{sum}(S)\) 的线段,初始有一些地方被切断,之后每个操作随机切。

首先考虑 \(n=1\) 的情况,设 \(p_k=\Pr\left[\forall i\in[1,k],X_{(i)}-X_{(i-1)}\le K\land L-X_{(k)}\le K\right]\),其中 \(X_1,X_2,\dots,X_k\)\((0,L)\) 上独立均匀的随机变量,\(X_{(0)},X_{(1)},\dots,X_{(k)}\)\(X_0=0,X_1,X_2,\dots,X_k\) 排序之后得到的序列。则答案是 \(\sum_{k\ge 0}(1-p_k)\)

\(w=\frac KL\),考虑计算 \(p_n\)。设 \(z_i=X_{(i)}-X_{(i-1)}\),则 \(z_1,z_2,\dots,z_n\) 的合分布的概率密度函数是

\[\begin{aligned} f(z_1,\dots,z_n)&=n!\left[z_i>0\land\sum_{i=1}^n z_i\le 1\right] \\ p_n&=n!\int\left[z_i\in(0,w)\land\sum_{i=1}^nz_i\in(1-w,1)\right]\text dz_1\dots\text dz_n \\ &=n!w^n\int\left[z_i\in(0,1)\land\sum_{i=1}^nz_i\in(\frac 1w-1,\frac 1w)\right]\text dz_1\dots\text dz_n \end{aligned} \]

后面的积分中的 \(\sum z_i\) 满足欧文–贺尔分布,其累积分布函数是

\[F(x)=\frac 1{n!}\sum_{k=0}^{\lfloor x\rfloor}(-1)^k\binom nk(x-k)^n \]

所以 \(p_n=n!w^n(F(\frac 1w)-F(\frac 1w-1))\)

\[\begin{aligned} 1-p_n&=1-\sum_{k=0}^x(-1)^k\binom nk(1-wk)^n +\sum_{k=0}^{x-1}(-1)^k\binom nk(1-w(k+1))^n \\ &=\sum_{k=1}^x(-1)^{k-1}\binom{n+1}k(1-wk)^n \end{aligned} \]

其中 \(x=\lfloor\frac 1w\rfloor\)。又有 \(\sum_{n\ge k}\binom nkx^n=x^k(1-x)^{-(k+1)}\pod{|x|<1}\),就可以计算了。

再考虑原问题,设 \(q_{m,j}\) 表示在第 \(j\) 条线段上做 \(m\) 次操作后仍未结束的概率。则

\[q_{m,j}=\sum_{k=1}^{\lfloor\frac{L_j}K\rfloor}(-1)^{k-1}\binom{m+1}k(1-\frac{kK}{L_j})^m \]

然后用 EGF 合成一下,

\[p_m=1-\sum_{\sum j_i=m}\frac{m!}{\prod j_i!}\prod_{r=1}^n(1-q_{j_r,r})\left(\frac{L_j}L\right)^{j_r} \]

\[\begin{aligned} Q_j(z)&=\sum_{m\ge 0}(1-q_{j_r,r})\left(\frac{L_j}{L}\right)^m\frac{z^m}{m!} \\ &=\exp\left(\frac{L_j}Lz\right)-\sum_{k=1}^{\lfloor\frac{L_j}K\rfloor}(-1)^{k-1}\sum_{m\ge k-1}\frac{m+1}{k!(m+1-k)!}\left(\frac{L_j-kK}{L}\right)^mz^m \end{aligned} \]

\(P(z)=\sum_{m\ge 0}p_m\frac{z^m}{m!}=\exp(z)-\prod_{i=1}^nQ_i\)。又有

\[\begin{aligned} \sum_{m\ge k-1}\frac{m+1}{k!(m+1-k)!}y^m&=\sum_{m\ge k}\frac{y^m}{k!(m-k)!}+\sum_{m\ge k-1}\frac{y^m}{(k-1)!(m+1-k)!} \\ &=\frac{y^k}{k!}\exp(y)+\frac{y^{k-1}}{(k-1)!}\exp(y) \\ \end{aligned} \]

所以

\[Q_j(z)=\exp(\frac{L_jz}L)\left(1-\sum_{k=1}^{\lfloor\frac{L_j}K\rfloor}(-1)^{k-1}\left(\left(\frac{L_j-kK}L\right)^k\frac{z^k}{k!}+\left(\frac{L_j-kK}L\right)^{k-1}\frac{z^{k-1}}{(k-1!)}\right)\exp\left(-\frac{kKz}L\right)\right) \]

使用 NTT 计算连乘积,即对于所有 \(0\le k\le L,0\le j\le\min(n,k)\) 计算 \(z^{k-j}\exp(1-\frac{kKz}L)\) 的系数。

对于 \(z^k\exp(Cz)\),其对应 OGF 为 \(k!\frac{(z/C)^k}{(1-Cz)^{k+1}}\),代入 \(z=1\) 并求和即可得到答案,时间复杂度 \(O(nL\log nL)\)

*CF1477D Nezzar and Hidden Permutations

题目描述:给定正整数 \(n\)\(m\) 个无序对 \((l_i,r_i)\),构造两个长为 \(n\) 的排列 \(p,q\) 使得 \(\forall i\in[1,m],(p_{l_i}-p_{r_i})(q_{l_i}-q_{r_i})>0\),且 \(\sum_{i=1}^n[p_i\ne q_i]\) 尽量大。\(T\) 组数据。

数据范围:\(\sum n,\sum m\le 5\cdot 10^5\)

有意思的构造题

solution

建无向图 \(G=(V,E)\),其中 \(V=[1,n]\cap\N,E=\{(l_i,r_i):i\in[1,m]\}\)

容易发现,对于点 \(x\),若 \(\deg(x)=n-1\),则 \(p_x=q_x\),这些点可以之后再填,直接忽略。

求出 \(G\) 的反图 \(G'\) 的一个无孤立点的子图。

对于一个连通块,可以将其分割成一堆菊花,每个菊花填成 \((1,2),(2,3),\dots,(k,k+1)\)\((k+1,1)\),保持值域连续即可不与其他点矛盾。

考虑如何分割。首先随便求个点覆盖边的匹配,对于不在匹配中的点,随便丢到连向的任意一个点的匹配中。

-CF1470E Strange Permutation

题目描述:给定长为 \(n\) 的排列 \(p\) 和正整数 \(c\),将所有选择若干个 \(r-l\) 之和 \(\le c\) 的不交区间 \([l,r]\) 分别翻转得到的排列按字典序排序后,\(q\) 次询问 \((i,j)\) 表示求第 \(j\) 个排列的第 \(i\) 个数,需判断无解。\(T\) 组数据。

数据范围:\(T\le 30,n\le 3\cdot 10^4,c\le 4,j\le 10^{18},\sum n,\sum q\le 3\cdot10^5\)

posted @ 2020-12-22 18:36  mizu164  阅读(504)  评论(1编辑  收藏  举报