A Ancestor

题意:给出两棵 nnn 个节点的树 A,BA,BA,B,A,BA,BA,B 树上每个节点均有一个权值,给出 kkk 个关键点的编号 x1,x2,⋯,xkx_1, x_2, \cdots, x_kx1​,x2​,⋯,xk​,问有多少种方案,使得去掉恰好一个关键点使得剩余关键点在树 AAA 上 lca 的权值大于树 BBB 上 lca 的权值。1≤k≤n≤1×1051 \leq k \leq n \leq 1\times 10^51≤k≤n≤1×105。

解法:由于 LCA 是可合并但没有逆的,因而对于这种挖去一个的情况,可以考虑维护序列的 lca 前缀和 preipre_iprei​ 与 LCA 的后缀和 sufisuf_isufi​。对于挖去 jjj 的情况就是 lca(prej−1,sufj+1){\rm lca}(pre_{j-1}, suf_{j+1})lca(prej−1​,sufj+1​)。时间复杂度 O(nlog⁡n)\mathcal O(n \log n)O(nlogn)。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
struct hh{int to,nxt;
};
int n,k,nd[N];
IL LL in(){char c;int f=1;while((c=getchar())<'0'||c>'9')if(c=='-') f=-1;LL x=c-'0';while((c=getchar())>='0'&&c<='9')x=x*10+c-'0';return x*f;
}
struct tree{hh e[N<<1];int num,fir[N],val[N],dep[N],fa[N][20],lf[N],rf[N];IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}void dfs(int u,int f){dep[u]=dep[f]+1,fa[u][0]=f;for(int i=0;fa[u][i];++i)fa[u][i+1]=fa[fa[u][i]][i];for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)dfs(v,u);}IL int Lca(int x,int y){if(dep[x]<dep[y]) swap(x,y);for(int i=18;~i;--i)if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];if(x==y) return x;for(int i=18;~i;--i)if(fa[x][i]^fa[y][i])x=fa[x][i],y=fa[y][i];return fa[x][0];}IL void init(){lf[1]=nd[1];for(int i=2;i<=k;++i)lf[i]=Lca(lf[i-1],nd[i]);rf[k]=nd[k];for(int i=k-1;i;--i)rf[i]=Lca(rf[i+1],nd[i]);}IL int get(int n){if(n==1) return val[rf[2]];if(n==k) return val[lf[k-1]];return val[Lca(lf[n-1],rf[n+1])];}
}A,B;
void solve(){n=in(),k=in();int ans=0;for(int i=1;i<=k;++i) nd[i]=in();for(int i=1;i<=n;++i) A.val[i]=in();for(int i=2;i<=n;++i) A.add(in(),i);for(int i=1;i<=n;++i) B.val[i]=in();for(int i=2;i<=n;++i) B.add(in(),i);A.dfs(1,0),B.dfs(1,0),A.init(),B.init();for(int i=1;i<=k;++i) ans+=(A.get(i)>B.get(i));printf("%d\n",ans);
}
int main()
{solve();return 0;
}

B Boss

题意:kkk 个城市有 nnn 个人去上班,每个人选一个城市上班,第 iii 个城市需要 eie_iei​ 个人去上班。第 iii 个人去第 jjj 个城市上班的代价为 ci,jc_{i,j}ci,j​,问最小化代价。n≤1×105n \leq 1\times 10^5n≤1×105,k≤10k \leq 10k≤10。

解法:如果不看范围,这题就是一个板子最小费用最大流——每个人向城市连边,流量为 111 费用为 ci,jc_{i,j}ci,j​;源点向每个人连边流量 111 费用 000,第 iii 个城市向汇点连边流量 eie_iei​ 费用 000。

但是此题边数达到了 1×1061\times 10^61×106,点数 1×1051\times 10^51×105,因而考虑模拟费用流的过程。注意到每次费用流的过程为:从源点找到汇点找一条最短路径,然后更新本条路径的流量,更新反向边的流量。而这题每次增广的流量必然为 111,因而这极大的限制了运行效率。

首先考虑做第一步的优化,将人初始分到一些城市去,优化第一步的 nnn 次增广。一个简单的方法就是全部安排到 111 号城市,利用 now{\rm now}now 数组来记录第 iii 个人现在工作的城市。通过这一安排将源点到人所在的节点完全固定下来,节约了大量的无效增广。

接下来考虑人员的流动。显然从源点到人这一部分是不可能再去增广了,唯一能实现流的增广的就是让人从一个城市到另一个城市上班——因为此时 111 号城市负载量过大,别的城市需要从 111 号城市挖人。这样的好处是将原本人与城市之间复杂的边关系转化成了城市与城市之间的关系,由于 kkk 非常小因而这降低了大量的复杂度。

那么依次考虑可能的增广操作——第 i,i∈[2,k]i,i \in [2,k]i,i∈[2,k] 个城市需要进行 eie_iei​ 次增广。对于这每一轮的增广,按照费用流的思想就是找到从源点到汇点的最短路,但是显然从源点到人、从城市到汇点这两部分对最短路是无意义的,因而考虑城市到城市之间的转移。对于每一对城市 (u,v)(u,v)(u,v),用一个堆记录之间所有可能的边——对于每个人 iii,若其原定城市为 uuu,则 (u,v)(u,v)(u,v) 之间有一条距离为 ci,v−ci,uc_{i,v}-c_{i,u}ci,v​−ci,u​ 的边,这个边代表了第 iii 个人的跳槽选择。表示这个人从 uuu 城市换到了 vvv 城市所改变的代价。因而暴力使用 spfa 来找到一条从 111 到 iii 的路径。在松弛的时候只需要找到每一对城市中边权最小的一条边即可,使用优先队列维护。

注意:整个过程可以认为是 111 号城市源源不断的给别的城市输送人,虽然其他城市之间也会互相换,但是这个换的意义是利用网络流中反悔边。所以源头一直都是 111。

找到最短路径之后就是费用流正常的操作——退流反向。这里的反向操作由于是在城市之间进行的,其意义是人在不同城市之间流动。例如对于一次增广中找到的路径 u→vu \to vu→v,若其边代表的人是 iii,则意义是第 iii 个人从第 uuu 个城市前往了 vvv 城市进行工作,因而更改 now\rm nownow 数组。同时由于 iii 的跳槽,他也有了新的 k−1k-1k−1 个跳槽选择,因而需要增设连向其他城市的 k−1k-1k−1 条边。

#include <bits/stdc++.h>
using namespace std;
const long long inf = 0x3f3f3f3f3f3f3f3fll;
using pli = pair<long long, int>;
priority_queue<pli, vector<pli>, greater<pli>> q[15][15];
//q[i][j] 内存储了边,这些边代表一个人的跳槽选择,以代价差为第一关键字,第二关键字仅用于表示人编号。
int main()
{int n, k;scanf("%d%d", &n, &k);vector<int> now(n + 1, 1), e(k + 1);//now[i]表示第i个人目前工作的城市for (int i = 1; i <= k;i++)scanf("%d", &e[i]);vector<vector<long long>> c(n + 1, vector<long long>(k + 1));for (int i = 1; i <= n;i++)for (int j = 1; j <= k;j++)scanf("%lld", &c[i][j]);long long ans = 0;for (int i = 1; i <= n;i++){ans += c[i][1];for (int j = 2; j <= k;j++)q[1][j].emplace(c[i][j] - c[i][1], i);}queue<int> que;for (int i = 2; i <= k;i++)for (int times = 1; times <= e[i];times++){vector<long long> dis(k + 1, inf);que.push(1);vector<int> pre(k + 1, 0), vis(k + 1, 0);vis[1] = 1;dis[1] = 0;while(!que.empty()){int tp = que.front();que.pop();vis[tp] = 0;for (int j = 2; j <= k;j++){while(!q[tp][j].empty() && now[q[tp][j].top().second] != tp)//当前人已经不在tp城市工作了,因而这条边废弃q[tp][j].pop();if (!q[tp][j].empty() && dis[j] > dis[tp] + q[tp][j].top().first){dis[j] = dis[tp] + q[tp][j].top().first;pre[j] = tp;if(!vis[j]){vis[j] = 1;que.push(j);}}}}ans += dis[i];int place = i;while(place != 1){int fa = pre[place];int ori_id = q[fa][place].top().second;now[ori_id] = place;//退流表现为人工作城市的变化for (int i = 1; i <= k;i++)if (i != place)//增设新的跳槽选择q[place][i].emplace(c[ori_id][i] - c[ori_id][place], ori_id);place = fa;}}printf("%lld", ans);return 0;
}

C Concatenation

题意:给定 nnn 个仅由 01234构成的字符串,问 n!n!n! 种排列中字典序最小的那一个。n≤2×106n \leq 2\times 10^6n≤2×106,∑∣si∣≤2×107\sum |s_i|\leq 2\times 10^7∑∣si​∣≤2×107。

解法:本题暴力使用 sort可以通过,排序规则为 a+b<b+aa+b<b+aa+b<b+a,其中 a,ba,ba,b 均为字符串。

考虑如何 O(n)\mathcal O(n)O(n) 的实现。首先将全部串插入 Trie 树,不妨设 ∣a∣<∣b∣|a|<|b|∣a∣<∣b∣,对于 a+b<b+aa+b<b+aa+b<b+a,如果 aaa 不是 bbb 的前缀,则可以根据 aaa 和 bbb 在 Trie 树上的 dfs 序大小来判断。

若 aaa 是 bbb 的前缀,则需要比较依次比较 b∣a∣+jb_{|a|+j}b∣a∣+j​ 与 bjb_{j}bj​ 的大小关系。这个可以通过对每个串进行一次 Z 函数得到每个串后缀和原串的 lcp\rm lcplcp 长度,从而实现 O(1)\mathcal O(1)O(1) 的比较。因而一个前缀的比较等效于对应的未匹配后缀。由于 bbb 的前缀个数仅 O(∣b∣)\mathcal O(|b|)O(∣b∣) 个的,因而对这些前缀排序仅需 O(∣b∣)\mathcal O(|b|)O(∣b∣) 的时间。

D Directed

题意:给定一个 nnn 个点以 111 为根的树,现在要从 sss 出发到 111 号节点。现在随机选择 kkk 条树边变成单向边,方向由儿子指向父亲。同时人在某一个节点以等概率选择出边,问期望多少步走到 111 号节点。

解法:对于一个有根树,从儿子节点 vvv 走到父节点 uuu,其期望步数为 Ev=2sizv−1E_v=2siz_v-1Ev​=2sizv​−1,其中 sizvsiz_vsizv​ 表示 vvv 的子树大小。

由此,如果没有改为单向边的操作,考虑从 sss 到 111 的期望步数,即是从 sss 一步一步走到其祖先直到 111 的期望步数之和。记从 sss 到 111 的路径为特殊路径 lll:s→v1→v2→⋯→1s \to v_1 \to v_2 \to \cdots \to 1s→v1​→v2​→⋯→1,基础答案为 ∑v∈l,v≠12sizv−1\displaystyle \sum_{v \in l, v \neq 1}2siz_v-1v∈l,v=1∑​2sizv​−1。

考虑有单向边的情况。如果一条边 (u,v)(u,v)(u,v) (不妨令 uuu 是 vvv 的祖先)作为单向边,那么其祖先的子树大小会受到影响(等效于从祖先视角看,uuu 这个子树被删除了),vvv 的子树都不会受到影响。进一步的,由于统计的期望步数仅由 lll 上的子树和决定,因而影响区间为 uuu 在 lll 上的最近祖先至 111 的这一条 lll 上的链。

考虑两种情况:

  1. (u,v)(u,v)(u,v) 为 lll 上的链。如果当前边不选,则对步数的贡献会减少 2sizu2siz_u2sizu​——向上的一步还是要计入。考虑从 uuu 开始到 111 的每一步的期望贡献:对于 uuu 的一级祖先 a1a_1a1​,u→a1u \to a_1u→a1​ 的期望步数要减少 2sizu2siz_u2sizu​,对于任何情况均成立,仅需要满足 v→uv \to uv→u 被选定,方案数为 (n−1−1k−1)\displaystyle {n-1-1 \choose k-1}(k−1n−1−1​),概率为 Pr⁡[1]=(n−1−1k−1)/(n−1k)\Pr[1]=\displaystyle {n-1-1 \choose k-1}\left/{n-1 \choose k}\right.Pr[1]=(k−1n−1−1​)/(kn−1​);对于 uuu 的二级祖先 a2a_2a2​,a1→a2a_1 \to a_2a1​→a2​ 的期望步数要减少 2sizu2siz_u2sizu​ 而非 2siza12siz_{a_1}2siza1​​,需要 u→a1u \to a_1u→a1​ 不是单向边,否则从 a2a_2a2​ 上升到 a3a_3a3​ 时,贡献为 2siza12siz_{a_1}2siza1​​ 而不是 2sizu2siz_u2sizu​,因而可选择范围为 Pr⁡[2]=(n−1−2k−1)/(n−1k)\Pr[2]=\displaystyle {n-1-2 \choose k-1}\left/{n-1 \choose k}\right.Pr[2]=(k−1n−1−2​)/(kn−1​)——v→uv\to uv→u 选定,且 u→a1u \to a_1u→a1​ 必不选;三级祖先需要满足 u→a1u \to a_1u→a1​ 和 a1→a2a_1 \to a_2a1​→a2​ 均不是单向边,因而概率为 Pr⁡[3]=(n−1−3k−1)/(n−1k)\displaystyle \Pr[3]={n-1-3 \choose k-1}\left/{n-1 \choose k}\right.Pr[3]=(k−1n−1−3​)/(kn−1​);因而,若 uuu 到 111 的路径长度为 depudep_udepu​,则总的概率为 ∑i=1depu−1Pr⁡[i]\displaystyle \sum_{i=1}^{dep_u-1} \Pr[i]i=1∑depu​−1​Pr[i],对答案的贡献需要减去 2sizu∑i=1depu−1Pr⁡[i]\displaystyle 2siz_u\sum_{i=1}^{dep_u-1} \Pr[i]2sizu​i=1∑depu​−1​Pr[i]。
  2. (u,v)(u,v)(u,v) 不是 lll 上的链。记 uuu 到链 lll 上的最近祖先为 aaa,则sizusiz_usizu​ 的贡献能被直接记录在 lll 上而非 uuu 到 aaa 的其他祖先上的概率为 u→au \to au→a 的路径上所有边都不允许是单向边。然后在 lll 上从 aaa 到 111 的分析和上面相同。因而其概率为 ∑i=depadepuPr⁡[i]\displaystyle \sum_{i=dep_a}^{dep_u} \Pr[i]i=depa​∑depu​​Pr[i],对答案的贡献为 2sizu∑i=depadepuPr⁡[i]\displaystyle 2siz_u\sum_{i=dep_a}^{dep_u} \Pr[i]2sizu​i=depa​∑depu​​Pr[i]。

总时间复杂度仅为遍历复杂度,O(n)\mathcal O(n)O(n)。

(队友代码)

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e6+3,p=998244353;
struct hh{int to,nxt;
}e[N<<1];
int n,k,s,num,fir[N],fac[N],ifac[N],bo[N],siz[N],dep[N],P[N],fa[N],bel[N],pp,ans;
IL int in(){char c;int f=1;while((c=getchar())<'0'||c>'9')if(c=='-') f=-1;int x=c-'0';while((c=getchar())>='0'&&c<='9')x=x*10+c-'0';return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
IL int ksm(int a,int b){int c=1;while(b){if(b&1) c=1ll*c*a%p;a=1ll*a*a%p,b>>=1;}return c;
}
void init(){fac[0]=1;for(int i=1;i<=n;++i) fac[i]=1ll*fac[i-1]*i%p;ifac[n]=ksm(fac[n],p-2);for(int i=n;i;--i) ifac[i-1]=1ll*ifac[i]*i%p;
}
IL int C(int n,int m){if(n<m) return 0;return 1ll*fac[n]*ifac[m]%p*ifac[n-m]%p;}
void dfs1(int u,int f){fa[u]=f,siz[u]=1,dep[u]=dep[f]+1;for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)if(v^f) dfs1(v,u),siz[u]+=siz[v];
}
void dfs2(int u,int bl){bel[u]=bl;for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)if(v^fa[u]){if(bo[v]) dfs2(v,v);else dfs2(v,bl);}
}
int main()
{int x,y;n=in(),k=in(),s=in(),init();for(int i=1;i<n;++i)x=in(),y=in(),add(x,y),add(y,x);dfs1(1,0);bo[s]=1;while(s!=1) bo[s=fa[s]]=1;dfs2(1,1);pp=ksm(C(n-1,k),p-2);for(int i=1;i<n;++i) P[i]=1ll*C(n-1-i,k-1)*pp%p,P[i]=mod(P[i-1]+P[i]);for(int i=2;i<=n;++i) if(bo[i]) ans=mod(ans+mod(2*siz[i]-1));for(int i=2;i<=n;++i)if(dep[i]>2){if(bo[i]) ans=mod(ans-1ll*P[dep[i]-2]*mod(2*siz[i])%p+p);else ans=mod(ans+p-1ll*mod(P[dep[i]-2]-P[dep[i]-dep[bel[i]]-1]+p)*mod(2*siz[i])%p);}printf("%d\n",ans);return 0;
}

F Fief

题意:有一个 nnn 个点 mmm 条边的无向图 GGG,qqq 次询问,每次询问给定 xi,yix_i,y_ixi​,yi​,表示一个长度为 nnn 的排列的第一项和最后一项,问是否能找到一个长度为 nnn 且首尾项为 xix_ixi​ 和 yiy_iyi​ 的排列,使得 ∀i∈[1,n−1]\forall i \in [1,n-1]∀i∈[1,n−1],排列的前 iii 项构成的子图和后 n−in-in−i 项均联通。n,q≤1×105n,q \leq 1\times 10^5n,q≤1×105,m≤2×105m \leq 2\times 10^5m≤2×105。

解法:考虑以割点进行缩图。

首先考虑几种特殊情况:

  1. 原图仅两个点,必然成立。
  2. 原图无割点,即全图为点双连通,则必然成立。
  3. 原图不连通,必然不成立。

对于一个朴素的连通且具有割点的图,缩完点后的图 G′G'G′ 必然为一条链——若 G′G'G′ 上存在一度数大于等于 333 的节点,记为 uuu,从 uuu 割出的某一连通分量出发,必然在随着排列的前 iii 项数量从小到大的扩张过程中扩张到 uuu,且后续还要继续扩张。而一旦扩张到 uuu,后 n−in-in−i 项不连通。

进一步的,考虑 xix_ixi​ 和 yiy_iyi​ 在 G′G'G′ 上的位置——若不是分属链的两端点,必然不成立——假设 xix_ixi​ 不在端点,则 yiy_iyi​ 必然会因为 xix_ixi​ 的阻断导致不能和两个链端点同时连通,对于 i=2i=2i=2 的情况就失败了。

代码来源于队友。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
using namespace std;
const int N=2e5+3;
struct hh{int to,nxt;
}e[N<<1];
int n,m,q,num,col,tp,sta[N],fir[N],dfn[N],low[N],val[N];
vector<int>e1[N],e2[N];
IL int in(){char c;int f=1;while((c=getchar())<'0'||c>'9')if(c=='-') f=-1;int x=c-'0';while((c=getchar())>='0'&&c<='9')x=x*10+c-'0';return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void tarjan(int u){dfn[u]=low[u]=++num,sta[++tp]=u;for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)if(!dfn[v]){tarjan(v),low[u]=min(low[u],low[v]);if(dfn[u]<=low[v]){++col;while(sta[tp+1]!=v)e1[sta[tp]].pb(col),e2[col].pb(sta[tp--]);e1[u].pb(col),e2[col].pb(u);}}else low[u]=min(low[u],dfn[v]);
}
IL void print(int op){for(int i=1;i<=q;++i) puts(op?"YES":"NO");exit(0);}
int main()
{int x,y,z;n=in(),m=in();for(int i=1;i<=m;++i)x=in(),y=in(),add(x,y),add(y,x);q=in(),num=0,tarjan(1),num=0;if(n==2) print(1);for(int i=1;i<=n;++i)if(!dfn[i]||e1[i].size()>2) print(0);if(col==1) print(1);for(int i=1;i<=col;++i){int deg=0;for(int j=0;j<e2[i].size();++j)deg+=e1[e2[i][j]].size()-1;if(deg>2) print(0);if(deg==1){++num;for(int j=0;j<e2[i].size();++j)if(e1[e2[i][j]].size()==1) val[e2[i][j]]=num;}}while(q--) puts(val[in()]+val[in()]^3?"NO":"YES");return 0;
}

G Geometry

题意:给定两个 nnn 个点和 mmm 个点的凸包和运动方向,问是否会发生碰撞,何时发生碰撞。3≤n,m≤5×1043 \leq n,m \leq 5\times 10^43≤n,m≤5×104。

解法:两个多边形速度为 v1⃗,v2⃗\vec{v_1},\vec{v_2}v1​​,v2​​ 等效于一个凸包以速度 v2⃗−v1⃗\vec{v_2}-\vec{v_1}v2​​−v1​​ 运动。那么问题就转化为了两个凸包的闵氏和与射线 (0,0)→v2⃗−v1⃗(0,0) \to \vec{v_2}-\vec{v_1}(0,0)→v2​​−v1​​ 有无交点,最近交点在哪里。此题与 https://www.luogu.com.cn/problem/P4557 很像。

I Ice Drinking

题意:给定 nnn,记随机变量 XXX 为一个长度为 nnn 的排列中满足 ai=ia_i=iai​=i 的个数,求所有排列中 E(Xk)E(X^k)E(Xk)。1≤n≤1×10181 \leq n\leq 1\times 10^{18}1≤n≤1×1018,0≤k≤n+50000 \leq k \leq n+50000≤k≤n+5000。

解法:由错排定义可得 n!=∑i=0n(ni)Dn−i\displaystyle n!=\sum_{i=0}^n {n \choose i}D_{n-i}n!=i=0∑n​(in​)Dn−i​。
E(Xk)=∑i=0nikPr⁡[x=i]=∑i=0nik(ni)Dn−i=∑i=0nDn−i(ni)∑j=0kj!(ij){kj}=∑i=0n∑j=0k(ni)(ij)j!{kj}Dn−i=∑i=0n∑j=0k(nj)(n−ji−j)j!{kj}Dn−i=∑j=0k(nj){kj}j!(∑i=jn(n−ji−j)Dn−i)=∑j=0k(nj){kj}j!(∑i=0n−j(n−ji)Dn−i−j)=∑j=0k(nj){kj}j!(n−j)!=∑j=0kn!j!(n−j)!{kj}j!(n−j)!=n!∑j=0n{kj}\begin{aligned} E(X^k)=&\sum_{i=0}^n i^k \Pr[x=i]\\ =&\sum_{i=0}^n i^k {n \choose i} D_{n-i}\\ =&\sum_{i=0}^n D_{n-i}{n \choose i}\sum_{j=0}^k j!{i \choose j}{k \brace j}\\ =&\sum_{i=0}^n \sum_{j=0}^k {n \choose i}{i \choose j}j!{k \brace j}D_{n-i}\\ =&\sum_{i=0}^n \sum_{j=0}^k {n \choose j}{n-j \choose i-j}j!{k \brace j}D_{n-i}\\ =&\sum_{j=0}^k {n \choose j} {k \brace j}j!\left(\sum_{i=j}^n{n-j \choose i-j} D_{n-i}\right)\\ =&\sum_{j=0}^k {n \choose j} {k \brace j}j!\left(\sum_{i=0}^{n-j}{n-j \choose i} D_{n-i-j}\right)\\ =&\sum_{j=0}^k{n \choose j}{k \brace j}j!(n-j)!\\ =&\sum_{j=0}^k\dfrac{n!}{j!(n-j)!}{k \brace j}j!(n-j)!\\ =&n!\sum_{j=0}^n {k \brace j} \end{aligned} E(Xk)==========​i=0∑n​ikPr[x=i]i=0∑n​ik(in​)Dn−i​i=0∑n​Dn−i​(in​)j=0∑k​j!(ji​){jk​}i=0∑n​j=0∑k​(in​)(ji​)j!{jk​}Dn−i​i=0∑n​j=0∑k​(jn​)(i−jn−j​)j!{jk​}Dn−i​j=0∑k​(jn​){jk​}j!(i=j∑n​(i−jn−j​)Dn−i​)j=0∑k​(jn​){jk​}j!(i=0∑n−j​(in−j​)Dn−i−j​)j=0∑k​(jn​){jk​}j!(n−j)!j=0∑k​j!(n−j)!n!​{jk​}j!(n−j)!n!j=0∑n​{jk​}​
因而对于 n≥kn \geq kn≥k,答案为贝尔数 ϖn=∑i=0k{ki}\displaystyle \varpi_n=\sum_{i=0}^k {k \brace i}ϖn​=i=0∑k​{ik​};对于 n<kn<kn<k,需要手动计算出最后几项斯特灵数,然后进行相减。

求贝尔数对 ppp 取模,可以参考本文https://www.cnblogs.com/lfri/p/11546313.html:利用 ϖn+p≡ϖn+ϖ(n+1)(modp)\varpi_{n+p}≡\varpi_n + \varpi_{(n+1)} \pmod pϖn+p​≡ϖn​+ϖ(n+1)​(modp),ppp 是质数,其时间复杂度为 O(p2log⁡n)\mathcal O(p^2 \log n)O(p2logn)。

对于斯特灵数的快速计算,对于本题 k−n≤5×103k-n \leq 5\times 10^3k−n≤5×103,可以采用二阶欧拉数 ⟨⟨nk⟩⟩\left\langle \left\langle \begin{matrix}n\\k\end{matrix} \right\rangle \right\rangle⟨⟨nk​⟩⟩:
{xx−n}=∑k=0n⟨⟨nk⟩⟩(x+n−1−k2n){x \brace x-n}=\sum_{k=0}^n \left\langle \left\langle \begin{matrix}n\\k\end{matrix} \right\rangle \right\rangle {x+n-1-k \choose 2n} {x−nx​}=k=0∑n​⟨⟨nk​⟩⟩(2nx+n−1−k​)
其中二阶欧拉数递推式为 ⟨⟨nk⟩⟩=(k+1)⟨⟨n−1k⟩⟩+(2n−1−k)⟨⟨n−1k−1⟩⟩\left\langle \left\langle \begin{matrix}n\\k\end{matrix} \right\rangle \right\rangle=(k+1)\left\langle \left\langle \begin{matrix}n-1\\k\end{matrix} \right\rangle \right\rangle+(2n-1-k)\left\langle \left\langle \begin{matrix}n-1\\k-1\end{matrix} \right\rangle \right\rangle⟨⟨nk​⟩⟩=(k+1)⟨⟨n−1k​⟩⟩+(2n−1−k)⟨⟨n−1k−1​⟩⟩。该时间复杂度为 O((k−n)2)\mathcal O((k-n)^2)O((k−n)2)。

此外还有一个做法,来源于 hos.lyric:
{nk}modp={(xy)modp,ifp∣k,y←kp,x←⌊n−yp−1⌋(xy){n−1−(p−1)x−yk}modp,ifp∤k,y←⌊kp⌋,x←⌊n−y−1p−1⌋{n \brace k} \bmod p= \begin{cases} \displaystyle {x \choose y} \bmod p,{\rm if}\ p|k,y \leftarrow \dfrac{k}{p},x \leftarrow \left \lfloor \dfrac{n-y}{p-1}\right \rfloor\\ \displaystyle {x \choose y}{n-1-(p-1)x-y \brace k} \bmod p, {\rm if}\ p \nmid k,y \leftarrow \left \lfloor \dfrac{k}{p}\right \rfloor,x \leftarrow \left \lfloor \dfrac{n-y-1}{p-1}\right \rfloor \end{cases} {kn​}modp=⎩⎨⎧​(yx​)modp,if p∣k,y←pk​,x←⌊p−1n−y​⌋(yx​){kn−1−(p−1)x−y​}modp,if p∤k,y←⌊pk​⌋,x←⌊p−1n−y−1​⌋​
具体证明可以参看http://matwbn.icm.edu.pl/ksiazki/aa/aa94/aa9413.pdf。

hos 对此的解释为:

I just guessed the pattern by looking the table for p=7p=7p=7. We can prove that Stirling1 mod p has a nice pattern because x(x−1)...(x−(p−1))≡xp−x(modp)x(x-1)...(x-(p-1)) \equiv x^p-x \pmod px(x−1)...(x−(p−1))≡xp−x(modp), and since Stirling2 is its inverse matrix, my intuition says it is correct.

如果提前预处理好 p2p^2p2 的斯特灵数表和组合数表,利用 Lucas 定理可以 O(log⁡n)\mathcal O(\log n)O(logn) 的求出。

由于本题的模数 862118861=857×997×1009862118861=857\times 997\times 1009862118861=857×997×1009,因而可以通过中国剩余定理,对于每个质因子单独考虑,然后合并。最终复杂度为 O(p2log⁡n+(k−n)2)\mathcal O(p^2\log n+(k-n)^2)O(p2logn+(k−n)2)。

#include <bits/stdc++.h>
using namespace std;
const int P = 862118861;
const int mod[3] = {857, 997, 1009};
class Bell
{const static int N = 1100;vector<vector<int>> Stirling;vector<int> bell;int mod;public:Bell(int mod){bell.resize(N + 5);Stirling.resize(N + 5);for (auto &i : Stirling)i.resize(N + 5);this->mod = mod;Stirling[0][0] = 1;for (int i = 0; i <= N; i++)for (int j = 1; j <= i; j++)Stirling[i][j] = (Stirling[i - 1][j - 1] + 1LL * j * Stirling[i - 1][j]) % mod;bell[0] = 1;for (int j = 1; j <= N; j++){bell[j] = 0;for (int k = 1; k <= j; k++)bell[j] = (bell[j] + Stirling[j][k]) % mod;}}int query(long long n){if(n < mod)return bell[n];vector<int> B(N + 5, 0), a(N + 5, 0), d(N + 5, 0);for (int i = 0; i <= mod;i++)B[i] = bell[i];int cnt = 0;while (n){a[cnt++] = n % mod;n /= mod;}for (int i = 1; i < cnt; i++)for (int j = 1; j <= a[i]; j++){for (int k = 0; k < mod; k++)d[k] = (B[k + 1] + i * B[k] % mod) % mod;d[mod] = (d[0] + d[1]) % mod;for (int k = 0; k <= mod; k++)B[k] = d[k];}return d[a[0]];}
};
class Binom
{int mod;const static int N = 1100;vector<vector<int>> C;public:void set(int mod){C.resize(N + 5);for (auto &i : C)i.resize(N + 5);this->mod = mod;for (int i = 0; i <= N;i++)C[i][i] = C[i][0] = 1;for (int i = 1; i <= N;i++)for (int j = 1; j < i;j++)C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;}long long query(long long n, long long m){if(n < mod && m < mod)return C[n][m];elsereturn C[n % mod][m % mod] * query(n / mod, m / mod) % mod;}
};
class Euler
{const static int N = 5000;vector<vector<int>> euler;int mod;Binom b;public:Euler(int mod){euler.resize(N + 5);for (auto &i : euler)i.resize(N + 5);b.set(mod);this->mod = mod;euler[0][0] = 1;for (int i = 1; i <= N;i++){euler[i][0] = 1;euler[i][i] = 0;}for (int i = 2; i <= N;i++)for (int j = 1; j < i;j++)euler[i][j] = (euler[i - 1][j] * (j + 1) + (2 * i - 1 - j) * euler[i - 1][j - 1]) % mod;}long long query_stirling(long long x, int n){long long ans = 0;for (int k = 0; k <= n;k++)ans = (ans + euler[n][k] * b.query(x + n - 1 - k, 2 * n) % mod) % mod;return ans;}
};
int main()
{long long n, m;scanf("%lld%lld", &n, &m);long long ans[3] = {0};for (int i = 0; i < 3;i++){Bell solve(mod[i]);ans[i] = solve.query(m);// printf("SOLVE:%d %lld\n", mod[i], ans[i]);}if (n < m)for (int i = 0; i < 3;i++){Euler solve(mod[i]);for (int j = 0; j < m - n;j++)ans[i] = (ans[i] - solve.query_stirling(m, j) + mod[i]) % mod[i];}long long res = (463753553LL * ans[0] + 376150155LL * ans[1] + 22215154LL * ans[2]) % P;printf("%lld", res);return 0;
}

H Hacker

题意:给定长度为 nnn 的模式串 SSS,和长度为 mmm 的权值数组 {wi}\{w_i\}{wi​}。对于一个长度为 mmm 的串,wiw_iwi​ 表示使用该串上第 iii 个字符与 SSS 匹配可以获得 wiw_iwi​ 的权值。kkk 次询问一个长度为 mmm 的串 TTT 与 SSS 的最大权值公共子串。n,m,k≤1×105,mq≤1×106n ,m,k\leq 1\times 10^5,mq\leq 1\times10^6n,m,k≤1×105,mq≤1×106。

解法:对 SSS 建立 SAM,然后对于每次询问都可以将 TTT 串拉上去跑,对于 TTT 串,每匹配一个字符,就可以通过 SAM 知道当前匹配了多长的字符串。因而问题转化成为了对于每个右端点 rrr,有一个左边界 lll,然后问处于某个区间的最大连续子段和。维护 www 的前缀和,利用区间查询最小值的线段树即可通过。复杂度 O(n+nlog⁡n)\mathcal O(n+n\log n)O(n+nlogn)。

#include <bits/stdc++.h>
using namespace std;
long long inf = 0x3f3f3f3f3f3f3f3fll;
class segment_tree
{int n;vector<long long> minimum;long long query(int place, int left, int right, int start, int end){if (start <= left && right <= end)return minimum[place];long long ans = inf;int mid = (left + right) >> 1;if (start <= mid)ans = min(ans, query(place << 1, left, mid, start, end));if (end > mid)ans = min(ans, query(place << 1 | 1, mid + 1, right, start, end));return ans;}public:void set(int n, vector<long long> &w){this->n = n;minimum.resize(4 * n + 10);function<void(int, int, int)> build = [&](int place, int left, int right){if (left == right){minimum[place] = w[left];return;}int mid = (left + right) >> 1;build(place << 1, left, mid);build(place << 1 | 1, mid + 1, right);minimum[place] = min(minimum[place << 1], minimum[place << 1 | 1]);};build(1, 0, n);}long long query(int l, int r){return query(1, 0, n, l, r);}
} tr;
vector<long long> pre;
class SAM
{const int shift = 97;struct node{int ch[26];int len;int father;long long cnt;node(){memset(ch, 0, sizeof(ch));len = father = cnt = 0;}} NIL;vector<node> t;int last, ind;void insert(int c){int p = last;int np = last = ++ind;t.push_back(NIL);t[np].len = t[p].len + 1;t[np].cnt = 1;for (; p && !t[p].ch[c]; p = t[p].father)t[p].ch[c] = np;if(!p)t[np].father = 1;else{int q = t[p].ch[c];if (t[p].len + 1 == t[q].len)t[np].father = q;else{int nq = ++ind;t.push_back(t[q]);t[nq].cnt = 0;t[nq].len = t[p].len + 1;t[q].father = t[np].father = nq;for (; p && t[p].ch[c] == q; p = t[p].father)t[p].ch[c] = nq;}}}public:SAM(string s){last = ind = 1;t.push_back(NIL);t.push_back(NIL);for (auto i : s)insert(i - shift);}long long query(string &s){int place = 1, len = 0, n = s.length();long long ans = 0;for (int i = 0; i < n;i++){while (place > 1 && !t[place].ch[s[i] - shift]){place = t[place].father;len = t[place].len;}if (!t[place].ch[s[i] - shift])continue;place = t[place].ch[s[i] - shift];len++;ans = max(ans, pre[i + 1] - tr.query(i + 1 - len, i + 1));}return ans;}
};
int main()
{cin.tie(0)->sync_with_stdio(0);cin.exceptions(cin.failbit);cin.tie(NULL);cout.tie(NULL);int n, m, k;long long x;cin >> n >> m >> k;pre.resize(m + 1);string s, t;cin >> s;SAM solve(s);for (int i = 1; i <= m;i++){cin >> x;pre[i] = pre[i - 1] + x;}tr.set(n, pre);for (int i = 1; i <= k;i++){cin >> t;cout << solve.query(t) << "\n";}return 0;
}

J Journey

题意:有 nnn 个十字路口,左转、掉头、直行都要等红灯,右转不用。这些十字路口相互连接成一个图,问从某个十字路口的某个方向开始到目的地的目标方向需要等几次红绿灯。n≤5×105n \leq 5\times 10^5n≤5×105。

解法:对于每个方向的道路看作一个点,边权为道路与道路之间的路口是否要等红绿灯,那么问题转化为边权仅有 0,10,10,1 的图的单源最短路问题,01 bfs 或者 Dijkstra 即可。复杂度 O(nlog⁡n)\mathcal O(n \log n)O(nlogn) 或 O(n)\mathcal O(n)O(n)。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+3;
struct hh{int to,nxt,w;
}e[N<<4];
struct kk{int x,y;
}S,T;
int n,c[N][4],dis[N][4];
IL LL in(){char c;int f=1;while((c=getchar())<'0'||c>'9')if(c=='-') f=-1;LL x=c-'0';while((c=getchar())>='0'&&c<='9')x=x*10+c-'0';return x*f;
}
IL kk turn(int x,int y){int pos=0;for(int i=1;i<4;++i)if(c[x][i]==y) pos=i;return (kk){x,pos};
}
deque<kk>q;
void bfs(){q.push_front(S);dis[S.x][S.y]=0;while(q.size()){kk u=q.front();q.pop_front();kk v=turn(c[u.x][u.y],u.x);int op=(v.y+1)%4;if(c[v.x][op]&&dis[v.x][op]>dis[u.x][u.y]){dis[v.x][op]=dis[u.x][u.y];q.push_front((kk){v.x,op});}for(int i=0;i<4;++i)if(i^op&&c[v.x][i]&&dis[v.x][i]>dis[u.x][u.y]+1){dis[v.x][i]=dis[u.x][u.y]+1;q.push_back((kk){v.x,i});}}if(dis[T.x][T.y]>1e9) printf("-1\n");else printf("%d\n",dis[T.x][T.y]);
}void solve(){n=in();memset(dis,63,sizeof(dis));for(int i=1;i<=n;++i)for(int j=0;j<4;++j)c[i][j]=in();int x=in(),y=in();S=turn(x,y);x=in(),y=in();T=turn(x,y);bfs();
}
int main()
{solve();return 0;
}

2022年牛客多校第三场补题记录相关推荐

  1. 2022 年牛客多校第四场补题记录

    A Task Computing 题意:给定长度为 nnn 的序列 {(wi,pi)}\{(w_i,p_i)\}{(wi​,pi​)},从中选出 mmm 项并重新排列得到子序列 {a1,a2,⋯,am ...

  2. 2021牛客多校第八场补题 D-OR

    链接:https://ac.nowcoder.com/acm/contest/11259/D 来源:牛客网 题目描述 There are two sequences of length n−1n-1n ...

  3. 2021牛客多校第五场补题

    B-Boxes 链接:https://ac.nowcoder.com/acm/contest/11256/B 来源:牛客网 题目描述 There're nn_{}n​ boxes in front o ...

  4. 2022 年杭电多校第八场补题记录

    A Theramore 题意:给定一个长度为 nnn 的 010101 串,每次可以选取一个奇数长度的连续子串进行位置上的翻转,问经过若干次操作能得到的字典序最小的串.n≤5×105n \leq 5\ ...

  5. 24dian(牛客多校第三场)

    24dian(牛客多校第三场) 题意: 给你n张牌,每张牌的大小为1 ~ 13,问这些牌与加减乘除任意组合(可以使用括号),且但所有的有效解在计算过程中都涉及到分数,即非整数,能否组成答案m,如果可以 ...

  6. 牛客多校第三场 B【Classical String Problem】

    牛客多校第三场 B[Classical String Problem] 链接:https://ac.nowcoder.com/acm/contest/5668/B 来源:牛客网 题目描述 Given ...

  7. 2019牛客多校第四场 I题 后缀自动机_后缀数组_求两个串de公共子串的种类数

    目录 求若干个串的公共子串个数相关变形题 对一个串建后缀自动机,另一个串在上面跑同时计数 广义后缀自动机 后缀数组 其他:POJ 3415 求两个串长度至少为k的公共子串数量 @(牛客多校第四场 I题 ...

  8. 牛客多校第三场A【Clam and fish】贪心

    A[Clam and fish]贪心 链接:https://ac.nowcoder.com/acm/contest/5668/A 来源:牛客网 题目: There is a fishing game ...

  9. 2019牛客多校训练营第一场 H题 HOR 题解

    题目描述: 输入描述: 输出描述: 示例1: 题解: 更多问题可关注牛客竞赛区,一个刷题.比赛.分享的社区. 传送门:https://ac.nowcoder.com/acm/contest/discu ...

最新文章

  1. 关于Anaconda的环境和包管理
  2. 头部ct能检查出什么_【安全用药】做CT检查时应注意什么?
  3. 网站搜索功能怎么实现_电商网站上的搜索功能是如何实现的?
  4. Django框架 之基础入门
  5. 鸿蒙os事例代码,鸿蒙HarmonyOS App开发造轮子之自定义圆形图片组件的实例代码
  6. 学位论文多级标题编号与图表编号
  7. 【PAT甲】1051 Pop Sequence (25分)判断出栈顺序的合法性
  8. python3随机种子的使用及理解
  9. 姓名国别分类代码:PyTorch深度学习实践 - Lecture_13_RNN Classifier
  10. Java基于WEB的商品库存管理系统
  11. 【MODIS数据处理#11】例六:绘制NDVI多年变化趋势空间分布图
  12. 计算机前置usb应用,usb前面不能用,教您解决电脑前置USB接口不能使用
  13. Python学习笔记-北京图灵学院-变量-20200528
  14. 泛泰binx和ota升级包下载工具Android版[2013.6.7提供源代码]
  15. 这可能是全网最全的数据仓库建设方法论!
  16. 给网站开发者推荐18个在线手册,值得收藏
  17. 【算法研究】 AEC 回音消除算法
  18. python能在pdf上加内容吗_在Python中向现有PDF文档添加文本
  19. VMware NSX 4.0安装、配置和升级实战
  20. canvas动画效果之星球守护

热门文章

  1. R语言 | 利用tushare获取股票k线、市值、换手率,市盈率等指标
  2. MySQL limit 1,1的含义
  3. 小试牛刀—猜数字游戏
  4. 第一课 大数据技术之Fink1.13的实战学习-部署使用和基础概念
  5. Brodmann 分区
  6. 用Python解决x的n次方
  7. 阿里云的云安全防护产品有哪些?都有什么作用?
  8. c语言报错 error:1d returned 1 exit status 。(原因:把main写成了mian)
  9. Python——星期的字典1——7
  10. DHTML 大全分析