#include<cstdio>#include<iostream>#include<algorithm>#define int long long#define RI register int#define CI const int&usingnamespace std;
constint N=100005,mod=998244353;
int n,a[5][N],id[5],ans;
inlinevoidinc(int& x,CI y){
if ((x+=y)>=mod) x-=mod;
}
signedmain(){
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j,k,s; for (scanf("%lld",&n),i=0;i<5;++i)
for (j=1;j<=n;++j) scanf("%lld",&a[i][j]),a[i][j]=5LL*a[i][j]+i;
for (i=0;i<5;++i) sort(a[i]+1,a[i]+n+1);
for (i=0;i<5;++i) for (s=0;s<(1<<4);++s)
{
int cur=0; for (j=0;j<4;++j) if ((s>>j)&1) ++cur;
if (cur!=2) continue; cur=0;
for (j=0;j<5;++j) if (i!=j) id[j]=(s>>cur)&1,++cur;
for (j=1;j<=n;++j)
{
int ret=1; for (k=0;k<5;++k) if (i!=k)
{
int coef=lower_bound(a[k]+1,a[k]+n+1,a[i][j])-a[k]-1;
if (id[k]) coef=n-coef; ret=1LL*ret*coef%mod;
}
inc(ans,1LL*ret*((a[i][j]-i)/5LL)%mod);
}
}
returnprintf("%lld",ans),0;
}
5|0G. Range NEQ
虽然算是 Counting,但恰好是牢闪勉强会一点的简单容斥和简单多项式,推了会也就会了
有禁区的排列计数直接上棋盘多项式,不难发现就是在一个边长为 n×m 的棋盘上放 n×m 个棋子,其中禁区为按主对角线排布的 n 个单元,每个单元为 m×m 的矩形
考虑容斥计数,枚举有 i 个棋子放在禁区中,同时令 f(i) 表示放 i 个棋子在禁区中的方案数,则答案为:
n×m∑i=0(−1)i×f(i)×(n×m−i)!
现在考虑计算 f(i),不难想到一个朴素的 DP,令 gi,j 表示处理了前 i 个单元,此时一共放了 j 个棋子的方案数
#include<cstdio>#include<iostream>#define RI register int#define CI const int&usingnamespace std;
constint N=1<<21,mod=998244353;
int n,m,f[N],fact[N],ifac[N];
inlinevoidinc(int& x,CI y){
if ((x+=y)>=mod) x-=mod;
}
inlinevoiddec(int& x,CI y){
if ((x-=y)<0) x+=mod;
}
inlineintquick_pow(int x,int p=mod-2,int mul=1){
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inlinevoidinit(CI n){
fact[0]=1; for (RI i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
ifac[n]=quick_pow(fact[n]); for (RI i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
inlineintC(CI n,CI m){
if (n<0||m<0||n<m) return0;
return1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod;
}
namespace Poly
{
int rev[N],lim,p;
inlinevoidinit(CI n){
for (lim=1,p=0;lim<=n;lim<<=1,++p);
for (RI i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<p-1);
}
inlinevoidNTT(int *f,CI opt){
for (RI i=0;i<lim;++i) if (i<rev[i]) swap(f[i],f[rev[i]]);
for (RI i=1;i<lim;i<<=1)
{
int m=i<<1,D=quick_pow(3,opt==1?(mod-1)/m:mod-1-(mod-1)/m);
for (RI j=0;j<lim;j+=m)
{
int W=1; for (RI k=0;k<i;++k,W=1LL*W*D%mod)
{
int x=f[j+k],y=1LL*f[i+j+k]*W%mod;
f[j+k]=f[i+j+k]=x; inc(f[j+k],y); dec(f[i+j+k],y);
}
}
}
if (!~opt)
{
int Inv=quick_pow(lim);
for (RI i=0;i<lim;++i) f[i]=1LL*f[i]*Inv%mod;
}
}
}
intmain(){
scanf("%d%d",&n,&m); init(n*m);
for (RI i=0;i<=m;++i) f[i]=1LL*C(m,i)*C(m,i)%mod*fact[i]%mod;
Poly::init(n*m); Poly::NTT(f,1);
for (RI i=0;i<Poly::lim;++i) f[i]=quick_pow(f[i],n);
Poly::NTT(f,-1); int ans=0;
//for (RI i=0;i<=n*m;++i) printf("%d%c",f[i]," \n"[i==n*m]);for (RI i=0;i<=n*m;++i)
{
int tmp=1LL*f[i]*fact[n*m-i]%mod;
if (i&1) dec(ans,tmp); elseinc(ans,tmp);
}
returnprintf("%d",ans),0;
}
6|0J. Make Convex Sequence
直接把做法写在题目名里了
首先不难发现题目中给的限制等价于找到的所有点 (i,Ai) 形成一个下凸壳
手玩发现我们对所有 (i,Ri) 求下凸壳后,检验它是否和每一个线段有交即可,这样一定是最优的
#include<bits/stdc++.h>usingnamespace std;
#define int long longusing pii = pair<int, int>;
constint N = 3e5+5;
int n, L[N], R[N];
int stk[N], tp=-1;
signedmain(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n;
for (int i=1; i<=n; ++i) cin >> L[i];
for (int i=1; i<=n; ++i) cin >> R[i];
auto check = [&](int p, int a, int b) {return (R[a]-R[p])*(b-p) < (R[b]-R[p])*(a-p);};
auto check2 = [&](int p, int a, int b) {return (L[a]-R[p])*(b-p) < (R[b]-R[p])*(a-p);};
for (int i=1; i<=n; ++i) {
while (tp>0 && !check(stk[tp-1], stk[tp], i)) --tp;
stk[++tp] = i;
}
bool ok=true;
int cur=1;
for (int i=1; i<=n; ++i) {
while (cur<=tp && stk[cur]<i) ++cur;
if (!check2(stk[cur-1], i, stk[cur])) {ok=false; break;}
}
cout << (ok ? "Yes\n" : "No\n");
return0;
}
7|0L. Many Products
看过题人数是个应该要会的 Counting,等过两天有时间再补吧
8|0M. Colorful Graph
只能说这题上来就发现是个经典的 DAG 最小可重复路径覆盖,然后偷懒了想去网上找标准的做法,结果发现会连出 O(n2) 条边被卡空间
后面只能自己想结果发现是个很 trivial 的建图,首先还是把原 DAG 用拆分二分图的模型来建,具体地:
S 向 i 连容量为 1 的边,且 n+i 向 T 连容量为 1 的边;
若原图中存在 x→y 的边,则 x 向 n+y 连容量为 ∞ 的边;
n+i 向 i 连容量为 ∞ 的边;
这样建图后跑出的最大流上的边一定在原 DAG 的路径覆盖上,求方案数的话就沿着选中的边 DFS 一下,用并查集把同色的点合并一下即可
#include<cstdio>#include<iostream>#include<vector>#include<queue>#include<cstring>#define RI register int#define CI const int&usingnamespace std;
constint N=7005,INF=1e9;
int n,m,x,y,dfn[N],low[N],idx,stk[N],top,vis[N],bel[N],tot,fa[N],col[N]; vector <int> v[N];
inlinevoidTarjan(CI now){
dfn[now]=low[now]=++idx; stk[++top]=now; vis[now]=1;
for (auto to:v[now]) if (!dfn[to]) Tarjan(to),low[now]=min(low[now],low[to]);
elseif (vis[to]) low[now]=min(low[now],dfn[to]);
if (low[now]==dfn[now])
{
bel[now]=++tot; vis[now]=0;
while (stk[top]!=now) bel[stk[top]]=tot,vis[stk[top--]]=0; --top;
}
}
namespace Network_Flow
{
constint NN=15005,MM=30005;
structedge {
int to,nxt,v,tp;
}e[MM<<1]; int cnt=1,head[NN],cur[NN],dep[NN],s,t;
inlinevoidaddedge(CI x,CI y,CI z){
//printf("%d -> %d (%d)\n",x,y,z); e[++cnt]=(edge){y,head[x],z,z}; head[x]=cnt;
e[++cnt]=(edge){x,head[y],0,-z}; head[y]=cnt;
}
#define to e[i].toinlineboolBFS(void){
memset(dep,0,(t+1)*sizeof(int));
dep[s]=1; queue <int> q; q.push(s);
while (!q.empty())
{
int now=q.front(); q.pop();
for (RI i=head[now];i;i=e[i].nxt)
if (e[i].v&&!dep[to]) dep[to]=dep[now]+1,q.push(to);
}
return dep[t];
}
inlineintDFS(CI now,CI tar,int dis){
if (now==tar) return dis; int ret=0;
for (RI& i=cur[now];i&&dis;i=e[i].nxt)
if (e[i].v&&dep[to]==dep[now]+1)
{
int dist=DFS(to,tar,min(dis,e[i].v));
if (!dist) dep[to]=INF;
dis-=dist; ret+=dist; e[i].v-=dist; e[i^1].v+=dist;
if (!dis) return ret;
}
if (!ret) dep[now]=INF; return ret;
}
inlinebooltravel(CI now,int& tar){
//printf("%d\n",now);if (now>tot)
{
for (RI i=head[now];i;i=e[i].nxt)
if (to==t&&e[i].tp>0&&e[i].v!=e[i].tp)
{
++e[i].v; tar=now; return1;
}
}
for (RI i=head[now];i;i=e[i].nxt)
{
//printf("to = %d; v = %d; tp = %d\n",to,e[i].v,e[i].tp);if (e[i].tp>0&&e[i].v!=e[i].tp)
{
++e[i].v;
if (travel(to,tar)) return1;
}
}
return0;
}
#undef toinlineintDinic(int ret=0){
while (BFS()) memcpy(cur,head,(t+1)*sizeof(int)),ret+=DFS(s,t,INF); return ret;
}
};
usingnamespace Network_Flow;
inlineintgetfa(CI x){
return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
intmain(){
scanf("%d%d",&n,&m);
for (RI i=1;i<=m;++i) scanf("%d%d",&x,&y),v[x].push_back(y);
for (RI i=1;i<=n;++i) if (!dfn[i]) Tarjan(i);
for (RI x=1;x<=n;++x) for (auto y:v[x])
if (bel[x]!=bel[y]) addedge(bel[x],tot+bel[y],INF);
//for (RI i=1;i<=n;++i) printf("%d%c",bel[i]," \n"[i==n]); s=0; t=2*tot+1; for (RI i=1;i<=tot;++i)
addedge(s,i,1),addedge(tot+i,t,1),addedge(tot+i,i,INF);
for (RI i=1;i<=tot;++i) fa[i]=i;
Dinic(); int num=0;
for (RI i=head[s];i;i=e[i].nxt)
if (e[i].tp>0&&e[i].v!=e[i].tp)
{
int st=e[i].to,tar;
++e[i].v; travel(st,tar);
//printf("%d %d\n",st,tar); fa[getfa(st)]=getfa(tar-tot);
}
for (RI i=1;i<=tot;++i) if (getfa(i)==i) col[i]=++num;
for (RI i=1;i<=n;++i) printf("%d ",col[getfa(bel[i])]);
return0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2023-09-16 CF677D Vanya and Treasure