AtCoder 问题乱做集1
- AGC014D Black and White Tree
*2266
题目大意: 两个人在 n 个节点的树上交替染色,先手染白色,后手染黑色。若染完之后有白点不与黑点相邻则先手胜,反之后手胜。求这棵树是先手必胜还是后手必胜。
n≤105
显然如果一个节点的儿子中有超过一个叶子,那么先手只需要选择它就必胜了。
否则我们发现必然存在节点只有一个儿子且其为叶子,那么这两个节点见合(就是先手后手各走一个),没有意义,可以直接删去。
只要不停重复这个流程即可。注意最后如果只剩 n 一个点也是先手胜(事实上,这也说明 n 为奇数先手必胜)
实现的话,只要在 DFS 时对 sizu=2 的情况不上传就好。

inline void dfs(int u,int fa){ siz[u]=1; for(int i=0;i<e[u].size();i++){ int v=e[u][i]; if(v==fa) continue; dfs(v,u); if(siz[v]>2) ok=1; else if(siz[v]<2) siz[u]+=siz[v]; } } int main(){ dfs(1,-1); ok|=(siz[1]!=2); puts(ok?"First":"Second"); return 0; }
- AGC030F Permutation and Minimum
*3474
题目大意:有一个 2n 个数的排列 A,有些位置数字未确定。你可以将剩下的数字填入,然后得到长度为 n 的序列 B : Bi=min{A2i−1,A2i}。求能得到的不同的 B 的数量。
1≤n≤300
评分虚高,然而我自己做仍然没做出来 /kk
首先分好类:
- A2i−1,A2i 均未确定
- A2i−1,A2i 确定一个
- A2i−1,A2i 均确定
第三种情况显然可以直接丢掉。
由于 Bi 在不断取 min,我们不妨考虑从高往低填数,考虑每个数的配对情况,这样做就没有后效性。
转移就是说,如果 x 为第一类数,那么他可以和第一类或第二类数匹配,也可以暂不匹配;如果 x 为第二类数,那么他可以和第一类匹配,也可以暂不匹配。注意第二类数被第一类数匹配时是有顺序的,以及第一类数和第一类数匹配的系数没必要在 DP 时算,可以在算完答案后再乘上 cnt!。

#include<bits/stdc++.h> using namespace std; int main(){ n=read(); for(int i=1;i<=n*2;i++) a[i]=read(); for(int i=1;i<=n;i++) if((~a[i*2-1]) && (~a[i*2])) vis[a[i*2-1]]=vis[a[i*2]]=-1; else if(~a[i*2-1]) vis[a[i*2-1]]=1; else if(~a[i*2]) vis[a[i*2]]=1; f[0][0][0]=1; for(int i=n*2;i;i--){ if(vis[i]==-1) continue; ff^=1; for(int j=0;j<=n;j++) for(int k=0;k<=n;k++){ f[ff][j][k]=0; if(vis[i]){ if(j) (f[ff][j][k]+=f[ff^1][j-1][k])%=mod; (f[ff][j][k]+=f[ff^1][j][k+1])%=mod; } else{ if(k) (f[ff][j][k]+=f[ff^1][j][k-1])%=mod; (f[ff][j][k]+=f[ff^1][j][k+1])%=mod; (f[ff][j][k]+=1ll*(j+1)*f[ff^1][j+1][k]%mod)%=mod; } } } ans=f[ff][0][0]; for(int i=1,j=0;i<=n;i++) if(a[i*2-1]==-1 && a[i*2]==-1) j++,ans=1ll*ans*j%mod; printf("%d\n",ans); return 0; }
- AGC035B Even Degrees
*2039
题目大意:给一张无向简单连通图,判断能否给每一条边定向,使得每一个点出度为偶数。如果可以,请输出任意一种方案。
2≤n≤105,n−1≤m≤105
人类智慧题。
首先 m 为奇数必然无解。
然后就是人类智慧:随便拉出一棵生成树,非树边随便定向,树边根据儿子节点的情况定向。容易发现这样可以保证除了生成树根节点之外所有节点满足条件,又因为所有点出度之和为 m,是偶数,所以根节点也必然满足条件。时间复杂度 O(n)。
这个自己做要怎么才能想到啊 /ll
- AGC003E Sequential operations on Sequence
*2983
题目大意:一个初始长度为 n 的数组,初始元素为 1∼n,现在给 Q 个操作,每次操作把数组长度变为 qi,新增的数为上一个操作后的数组的重复。问 Q 次操作后 1∼n 每个数出现了多少次。
n,Q≤105,qi≤1018
抽象思维题,或者说是 key observation problem。
首先如果 i<j,qi>qj,那么 qi 是没有意义的。用单调栈让 qi 单调上升。
然后考虑数组长度从 qi 到 qi+1 的过程,相当于先把当前数组复制 ⌊qi+1qi⌋ 次,再处理剩下的 x=qi+1modqi 个数。然后我们有一个重要的观察:对于一个 j 满足 qj<d,qj+1>d,qj 与 d 的关系与 qi 与 qi+1 的关系完全相同。每次二分找到 j 后倒序递归处理,差分数组维护。时间复杂度 O(nlog2n)。

inline void solve(ll x,int i){ if(stc[1]>x){ c[1]+=f[i]; c[x+1]-=f[i]; return; } int y=upper_bound(stc+1,stc+top+1,x)-stc-1; f[y]+=x/stc[y]*f[i]; if(x%stc[y]) solve(x%stc[y],i); } int main(){ f[top]=1; for(int i=top;i>=2;i--) f[i-1]+=stc[i]/stc[i-1]*f[i],solve(stc[i]%stc[i-1],i); c[1]+=f[1]; c[stc[1]+1]-=f[1]; for(int i=1;i<=n;i++) c[i]+=c[i-1],printf("%lld\n",c[i]); return 0; }
- ABC152F Tree and Constraints
*1965
题目大意:给你一棵 n 个节点的树,你要给这棵树黑白染色,并且符合 m 条限制,每条限制给定 u 和 v,需要满足 u 到 v 的路径上至少有一个黑色边,问有多少种染色方案。
n≤50,m≤20
看到 m≤20,考虑直接状压。
预处理一个状态数组 sta 表示对于一条边,将其染为黑色能满足条件的限制状态。对于第 i 个限制 (u,v),我们直接把路径上所有边拉出来,并把 i 并给这些边的状态。
然后你发现这个问题已经和树的形态无关了,直接设 fs 表示处理到当前边,已经满足的限制状态为 s 的方案数。对于当前边 i 而言,转移就是:
fs ∪ stai=fs ∪ stai+fs
直接转移即可,时间复杂度 O(n×2m)。

inline void dfs3(int u){ for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa[u]) continue; dfs3(v); for(int j=(1<<m)-1;~j;j--) f[sta[v]|j]+=f[j]; } } int main(){ n=read(); for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v); add(v,u); } dfs1(1); dfs2(1,1); m=read(); for(int i=1;i<=m;i++){ int u=read(),v=read(),g=lca(u,v); while(u!=g) sta[u]|=1<<i-1,u=fa[u]; while(v!=g) sta[v]|=1<<i-1,v=fa[v]; } f[0]=1; dfs3(1); printf("%lld\n",f[(1<<m)-1]); return 0; }
- ABC248G GCD cost on the tree
*2514
题目大意:给定一棵 n 个节点的树,每个结点上有一个权值 ai。对于每条至少包含两个点的链,它的贡献为链上点的数量(包括端点) × 链上所有权值的最大公约数。
求树上所有链的贡献之和。
2≤n≤105,1≤ai≤105
考虑枚举 GCD 的值为 t,求出每个 t 对应的方案数 ft。
直接求出有多少路径的 GCD 等于它不好求,我们考虑先求出满足 GCD 的值为 t 的倍数的方案数 gt,则 ft=gt−∑fit(i>1)。
对于 gt,我们先把所有满足 t | ai 的 i 拉出来建出一个森林,在每一棵树上做树形dp:
cnti 表示 i 的子树内,有一端点为 i 的链的个数。
leni 表示 i 的子树内,有一端点为 i 的链的链长之和(准确的说应该是包括的点个数,没啥差别)。
resi 表示 i 的子树内,经过 i 的所有长度大于 1 的链的长度之和。
那么 gt=∑resi。
对于边 (u,v),有转移:
resu=resu+cntu×lenv+cntv×lenu
前面的 resu 是因为 v 子树内可以不选(但是 u 必须选),后面是计算两棵子树合并,子树根处两条链拼起来的总长度。
lenu=lenu+lenv+cntv
v 子树内的链会和 u 接起来,长度加 1。
cntu=cntu+cntv
与 lenu 同理。
然后就做完了,时间复杂度 O(VlogV+n√V)。

#include<bits/stdc++.h> using namespace std; inline void dfs(int u,int tt){ cnt[u]=1; len[u]=1; res[u]=0; col[u]=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(!col[v]) continue; dfs(v,tt); res[u]=(res[u]+(1ll*cnt[u]*len[v]%mod+1ll*cnt[v]*len[u]%mod)%mod)%mod; len[u]=(len[u]+len[v]+cnt[v])%mod; cnt[u]=cnt[u]+cnt[v]; } (ans[tt]+=res[u])%=mod; } int main(){ n=read(); for(int i=1;i<=n;i++){ int x=read(); m=max(m,x); for(int j=1;j*j<=x;j++) if(x%j==0){ g[j].emplace_back(i); if(j*j<x) g[x/j].emplace_back(i); } } for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v); add(v,u); } for(int i=m;i;i--){ if(g[i].empty()) continue; for(int j=0;j<g[i].size();j++) col[g[i][j]]=1; for(int j=0;j<g[i].size();j++) if(col[g[i][j]]) dfs(g[i][j],i); for(int j=i+i;j<=m;j+=i) (ans[i]+=mod-ans[j])%=mod; } for(int i=1;i<=m;i++) (sum+=1ll*ans[i]*i%mod)%=mod; printf("%d\n",sum); return 0; }
- ARC083F Collecting Balls
*3541
题目大意:
n×n 的正方形上有 2n 个小球,第 i 个在 (xi,yi)。
有 n 个 A 类机器人,第 i 个在 (0,i),有 n 个 B 类机器人,第 i 个在 (i,0)。
启动一个 A 类机器人后,它会向右走,将碰到的第一个球收集起来,并返回起点。启动一个 B 类机器人后,它会向上走,将碰到的第一个球收集起来,并返回起点。
只有上一个机器人返回起点后,下一个机器人才会被启动。每个机器人只能启动一次。
问有多少种启动机器人的顺序,能够收集完所有小球。
n≤105
神中神题,想了半年点开题解发现思路连个边都没沾上 /ll
首先,我们把支配关系转化到图上,对于在 (x,y) 的球,我们给第 x 行与第 y 列连无向边。
这样我们把问题转化为,我们需要给每条边选一个端点,让这个端点支配这条边。同时,同时如果边 (u,v) 被 u 支配,那么对于所有 d<v,(u,d) 必须在 (u,v) 之前被 d 支配。
那么首先,如果这个图不是基环树森林,答案一定为 0。
那么现在考虑那个支配顺序关系。如果我们先把每棵基环树支配好(显然有两种方式,即和环上的支配顺序有关),然后把那个支配关系连上边,那么这又会变成一个森林。
然后答案就是这个森林的拓扑序数量,即 2n!∏sizi。证明就是因为对于 i 而言,有 sizi−1 个点不能在它前面,那么合法方案就要乘一个 1sizi 的系数。
当然直接枚举定向关系再把每种情况的答案加起来复杂度就上天了,这里我们考虑加法原理的本质,即每棵基环树有两种定向方式,设其算出来的系数为 A,B,那么我们算完了其它部分的总系数 T 后,合并起来的系数就应该是 (A+B)T。因此我们只需要对每棵基环树计算这个系数即可。
精细实现可做到 O(n),反正我写的挺难受,可以看看完整代码感受下(bushi)。

#include<bits/stdc++.h> using namespace std; const int N=200005; const int mod=1000000007; inline int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x*f; } int n,fac[N],inv[N],ans; int dfn[N],low[N],idx,bri[N<<1],col[N],tr,ecnt; vector<int>g[N]; int cnt,vis[N],b[N],circnt,iscir[N],ctr[N],cir[N],deg[N],siz[N]; struct Edge{ int to,nxt; }e[N<<1],c[N]; int heade[N],tote,headc[N],totc; inline void adde(int u,int v){ e[++tote]={v,heade[u]}; heade[u]=tote; } inline void addc(int u,int v){ c[++totc]={v,headc[u]}; headc[u]=totc; } inline void tarjan(int u,int fa){ dfn[u]=low[u]=(++idx); for(int i=heade[u];i;i=e[i].nxt){ int v=e[i].to; if(!dfn[v]){ tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) bri[i]=bri[i^1]=1; } else if(v!=fa) low[u]=min(low[u],dfn[v]); } } inline void dfs1(int u,int fa){ col[u]=tr; g[tr].emplace_back(u); for(int i=heade[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa || bri[i]) continue; if(col[v]){ ecnt++; continue; } dfs1(v,u); } } inline void dfs2(int u){ vis[u]=1; b[++cnt]=u; for(int i=heade[u];i;i=e[i].nxt){ int v=e[i].to; if(vis[v]) continue; dfs2(v); } } inline void dfs3(int u,int fa){ for(int i=heade[u];i;i=e[i].nxt){ int v=e[i].to; if(iscir[v] || v==fa) continue; ctr[v]=u; dfs3(v,u); } } inline void dfs4(int u,int fa){ for(int i=heade[u];i;i=e[i].nxt){ int v=e[i].to; if(v>=ctr[u]) continue; addc(u,v); deg[v]++; } for(int i=heade[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa || iscir[v]) continue; dfs4(v,u); } } inline int dfs5(int u,int fa){ siz[u]=1; int res=1; for(int i=headc[u];i;i=c[i].nxt){ int v=c[i].to; if(v==fa) continue; res=1ll*res*dfs5(v,u)%mod; siz[u]+=siz[v]; } res=1ll*res*inv[siz[u]]%mod; return res; } inline int calc(){ for(int i=1;i<=circnt;i++) dfs4(cir[i],-1); int ret=1; for(int i=1;i<=cnt;i++) if(!deg[b[i]]) ret=1ll*ret*dfs5(b[i],-1)%mod; return ret; } inline int solve(int rt){ cnt=circnt=0; dfs2(rt); for(int i=1;i<=cnt;i++) if(iscir[b[i]]) dfs3(b[i],-1),cir[++circnt]=b[i]; for(int i=1;i<=cnt;i++) headc[b[i]]=deg[b[i]]=0; totc=0; for(int i=1;i<circnt;i++) ctr[cir[i]]=cir[i+1]; ctr[cir[circnt]]=cir[1]; int ret=calc(); for(int i=1;i<=cnt;i++) headc[b[i]]=deg[b[i]]=0; totc=0; for(int i=1;i<circnt;i++) ctr[cir[i+1]]=cir[i]; ctr[cir[1]]=cir[circnt]; (ret+=calc())%=mod; return ret; } int main(){ n=read(); tote=1; for(int i=1;i<=n*2;i++){ int u=read(),v=read(); adde(u,v+n); adde(v+n,u); } fac[0]=fac[1]=inv[1]=1; for(int i=2;i<=n*2;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; for(int i=1;i<=n*2;i++) if(!dfn[i]) tarjan(i,-1); for(int i=1;i<=n*2;i++) if(!col[i]){ tr++; ecnt=0; dfs1(i,-1); if(ecnt>2) return puts("0"),0; } for(int i=1;i<=tr;i++){ if(g[i].size()==1) continue; for(int j=0;j<g[i].size();j++) iscir[g[i][j]]=1; } ans=1; for(int i=1;i<=n*2;i++) if(!vis[i]) ans=1ll*ans*solve(i)%mod; ans=1ll*ans*fac[n*2]%mod; printf("%d\n",ans); return 0; }
- ABC259F Select Edges
*1961
题目大意:给定一棵 n 个节点的树,每条边有一个权值 wi。要求选择一些边,使得每个节点 i 相邻的边中被选中的不超过 di 条,请求出最大边权和。
n≤3×105,|ai|≤109
这个都没独立做出来 /qd
考虑 DP。设 fu,0/1 表示 u 向/不向父亲节点连边时,在子树内得到的最优值(其实就是 fu,0 在 u 这里能多连一个儿子),用堆随便转移一下就好,时间复杂度 O(nlogn)。
- AGC032D Rotation Sort
*2602
题目大意:给定一个长度为 n 的排列 a, 你可以花费 A 使一个区间最左边的数跑到最右边, 或者花费 B 的代价使最右边到最左边, 求把整个序列变成升序的最少花费。
n≤5000,1≤A,B≤109
严重虚高,这个D比某些场次的AB简单的多...
考虑这个操作的本质,就是把一个数字往后插入,耗费 A 的代价;把一个数字往前插入,耗费 B 的代价。
那显然一个数最多操作一次,不操作的部分一定单调递增,那我们直接 DP 那个不操作的部分就好了。
设 fi,j 表示当前处理到第 i 个数,留下的最后一个数为 j 的最小代价。
fi,j={fi−1,j+Aai>jfi−1,j+Bai<jfi−1,k (k<ai)ai=j
直接转移即可,时间复杂度 O(n2)。
用一个线段树做区间加区间求 min 还可以做到 O(nlogn)。
- AGC010C Cleaning
*2346
题目大意:一棵树,第 1 个节点上有 ai 个石头,每次选择两个叶子节点(度数为 1 的节点),将路径上经过的(包括起点终点)所有节点上都取走一个石头,如果路径上有一个点上没石头这个操作就不能进行,问能不能取完所有石头。
2≤n≤105,0≤ai≤109
被薄纱,对着题解看了半年 /ll
考虑 u 子树内来自不同儿子的路径:
- 如果在内部匹配,则路径条数减少 2,au 减少 1。
- 如果向上匹配,则路径条数和 au 都减少 1。
因此路径条数小于 au 则无解。
如果重儿子(路径最多)的路径条数大于 au,则这个儿子传上来的路径无法匹配完,无解。
解方程 cnt−2x=au−x 解得 x=cnt−au
向上传递即可,注意如果解出来 x 不合法也是无解。如果最后做完之后根节点上还有没匹配的路径,也是无解。
注意要找到一个度数大于 1 的点作为根,因为上面的决策都是对于非根节点的。

inline bool dfs(int u,int fa){ if(deg[u]==1){ siz[u]=a[u]; return 1; } int sonmx=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa) continue; if(!dfs(v,u)) return 0; siz[u]+=siz[v]; sonmx=max(sonmx,siz[v]); } if(siz[u]<a[u]) return 0; if(sonmx>a[u]) return 0; // a-x = sum-2x x=sum-a sum-2*sum+2a siz[u]=2*a[u]-siz[u]; if(siz[u]<0) return 0; return 1; }
- ARC121F Logical Operations on Tree
*2940
题目大意:给定一棵树,给每个点填 0 或 1,给每条边填 AND 或 OR,在所有 22n−1 种填法中,计数有多少种满足存在一种缩边的顺序,使得每次把一条边的两个端点缩成一个点,权为原端点与边的运算值,最终点的权为 1。
2≤n≤105
感觉不难,有些虚高。自己瞎做一通搞出来了,比较感动。
注意到如果有叶子节点为 1 且其到父亲的点标了 OR,那么其最终边权一定可以为 1。
然后就直接 DP 求边权为 0 的方案数,用 fu,0/1 表示 u 的子树内最后算出来权值为 0/1 的方案数,转移的时候不讨论 av=1,opt=OR 的情况即可。

inline void dfs(int u,int fa){ int p0=1,q0=0,q1=1,son=0; // p0: u 这个点填 0 结果一定为 0 q0,q1:u 这个点填 1 当前结果为 0/1 for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa) continue; dfs(v,u); son++; p0=1ll*p0*(f[v][0]*2%mod+f[v][1])%mod; q0=(1ll*q0*(f[v][0]*2%mod+f[v][1])%mod+1ll*q1*f[v][0]%mod)%mod; q1=1ll*q1*(f[v][0]+f[v][1])%mod; } f[u][0]=(p0+q0)%mod,f[u][1]=q1; }
- AGC043D Merge Triplets
*2708
题目大意:
给定如下构造生成长度为 3n 的排列 P 的方法:
- 先生成一个长度为 3n 的排列 A。然后将 ∀k∈[0,N−1],A3k+1,A3k+2,A3k+3 分成一块。
- 有 N 个指针,初始指向每个块的第一个数。
- 每次选择所有指针指向的数中最小的数删除,然后放到 P 的末尾。之后指向被删除的数后移一个位置。若移出块了,则删除这个指针。
求一共能生成的 P 有多少种。
n≤2000
神仙结论题,完全不会 /ll
大力找(he)规(ti)律(jie),发现生成排列合法的充要条件:
- 不存在长度大于 3 的前缀 max 相同段,即不存在 Pi>max{Pi+1,Pi+2,Pi+3}
- 长度为 2 的前缀 max 相同段个数不大于长度为 1 的前缀 max 相同段个数。
必要性很显然(但是我就是想不到结论 /ll),都是因为一个块里只有 3 个数。充分性也很好证,你可以发现这样的排列一定可以直接构造出对应的原排列。
然后就是 DP 一下,设 fi,j 表示填到 i,有 j 个长度为 2 的段。转移的时候分别考虑填长度为 1,2,3 的段:
fi,j=fi−1,j+fi−2,j−1×(i−1)+fi−3,j−1×(i−2)×(i−1)
解释的话就是说段的第一个位置必须填当前最大值,剩下的位置可以随便选
直接转移即可。时间复杂度 O(n2)。
- ARC101E Ribbons on Tree
*2913
题目大意:给定一个大小为 n 的树,保证 n 为偶数。
您需要给树上的点两两配对,并将配对点的简单路径覆盖,定义一个配对方案合法当且仅当所有边都被覆盖到(一次或多次),求合法方案数。
n≤5000
感觉虚高,这个题没独立做出来,思维还差的多 /ll
先考虑一个 O(n3) 的简单做法:
设 fu,i 表示 u 的子树内还有 i 个点要向上匹配的方案数。我们发现如果 (u,v) 这条边要被覆盖到,那么 v 的子树内必然有点要向上匹配。
因此合并转移的时候枚举 u 子树内的点数 i,v 子树内的点数 j(注意 j>1),以及匹配的对数 k,有转移:
newfu,i+j=newfu,i+j+fu,i×fv,j×(ik)×(jk)×k!
答案即为 f1,0。但是这样做系数很复杂,感觉完全没有优化的空间。
正难则反,考虑容斥求不合法方案。
对于不合法方案,我们可以看作断开一个边集 S,然后给每个联通块内两两配对,得到方案数 T(S),对答案的贡献即为 (−1)k×T(S)。
容易发现的是,大小为 2t 的联通块两两配对,方案数 g(t)=∏ti=1(2i−1)
重新设状态 fu,i 表示在 u 子树内,u 所在联通块答案为 i 的方案数。
考虑边的钦定情况,有转移方程:
newfu,i=newfu,i+fu,i×fv,j×(−g(j)) 钦定该边被断
newfu,i+j=newfu,i+j+fu,i×fv,j 该边没被断
答案为 ∑ni=1f1,i×g(i)。时间复杂度 O(n2)。

inline void dfs(int u,int fa){ siz[u]=1; f[u][1]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa) continue; dfs(v,u); for(int j=1;j<=siz[u];j++) ff[j]=f[u][j],f[u][j]=0; for(int j=1;j<=siz[u];j++) for(int k=1;k<=siz[v];k++){ (f[u][j]+=mod-1ll*ff[j]*f[v][k]%mod*g[k]%mod)%=mod; (f[u][j+k]+=1ll*ff[j]*f[v][k]%mod)%=mod; } siz[u]+=siz[v]; } }
- ABC216G 01Sequence
for(int i=1;i<=m;i++){ int l=read(),r=read(),x=read(); x=r-l+1-x; // a[r]<=a[l-1]+x add(l-1,r,x); } for(int i=1;i<=n;i++){ // a[i]<=a[i-1]+1 a[i-1]<=a[i] add(i-1,i,1); add(i,i-1,0); } dijkstra(0); for(int i=1;i<=n;i++) printf("%d%c",1-(dis[i]-dis[i-1]),i==n?'\n':
*1963
题目大意:构造一个长度为 n 的 01 序列,满足 m 个限制 (li,ri,xi):在 [li,ri] 这段区间内,序列上 1 的个数不小于 xi。你需要保证你的方案中包含 1 的个数最小。
数据保证有解。
1≤n,m≤2×105
简单差分约束,想到前缀和就会了,对前缀和列出不等式跑一下最短路即可。
注意 duliu 出题人卡 SPFA,所以必须确认你的连边方式是非负的,且能保证得到 1 的个数最少。我的方法是维护 0 意义下的前缀和求最短路。

for(int i=1;i<=m;i++){ int l=read(),r=read(),x=read(); x=r-l+1-x; // a[r]<=a[l-1]+x add(l-1,r,x); } for(int i=1;i<=n;i++){ // a[i]<=a[i-1]+1 a[i-1]<=a[i] add(i-1,i,1); add(i,i-1,0); } dijkstra(0); for(int i=1;i<=n;i++) printf("%d%c",1-(dis[i]-dis[i-1]),i==n?'\n':' ');
- ABC221G Jumping sequence
*2914
题目大意:
有一个无限大的平面直角坐标系,初始时你在 (0,0) 处。给你一个长度为 n 的序列 d,你可以移动 n 步,每一步可以选择向上下左右四个方向中的一个移动 di 的距离。你想在 n 步结束后位于 (A,B) 位置,问是否存在这样的方案,如果存在需输出任意一种方案。
n≤2000,di≤1800,|A|,|B|≤3.6×106
纯纯的套路题。
将坐标轴旋转 45∘,(A,B) 变化为 (A−B,A+B)。然后操作就相当于从 (x,y) 走到 (x±di,y±di),这样两维就独立了。
对于每一维我们可以用 bitset 背包解决,但是要输出方案,需要开 n 个值域在 −3.6×106∼3.6×106 的 bitset,然后你发现它开不下。再改一下,发现第二维不开两维就不会爆,有点难受。
那咋办呢?我们注意到在随机生成的数据下,我们的第二位在背包过程中不会访问到比较大的负下标(就是当值减到比较小的时候,我们直接不管它了)。但是如果数据精心构造一下,让合法方案的减法全部堆到前面,你这样就出事了。
于是我把序列 shuffle 一下,这下你该卡不掉我了吧(
还有就是一个比较有趣的细节:在随机数据下,解可能会很多,因此你在输出方案时不能一直往一个方向跑,不然有可能会 RE(
时间复杂度 O(n|A|w)。

#include<bits/stdc++.h> using namespace std; const int N=2005; const int V=3650001; const char ch[5]={'L','D','U','R'}; inline int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x*f; } int n,rA,rB,A,B,ans[N]; struct Opt{ int x,y; }d[N]; bitset<V>f[N]; mt19937 rnd(10086001); inline void solve(int X,int op){ int flag=0; if(X<0) X=-X,flag=op; if(X>=V){ puts("No"); exit(0); } f[0].reset(); f[0][50000]=1; for(int i=1;i<=n;i++) f[i] = (f[i-1]<<d[i].x) | (f[i-1]>>d[i].x); if(!f[n][50000+X]){ puts("No"); exit(0); } for(int i=n;i;i--){ if(X<0){ if(f[i-1][50000+X+d[i].x]) X+=d[i].x,ans[d[i].y]|=flag; else X-=d[i].x,ans[d[i].y]|=flag^op; } else{ if(f[i-1][50000+X-d[i].x]) X-=d[i].x,ans[d[i].y]|=flag^op; else X+=d[i].x,ans[d[i].y]|=flag; } } } int main(){ n=read(),rA=read(),rB=read(); A=rA-rB,B=rA+rB; for(int i=1;i<=n;i++) d[i].x=read(),d[i].y=i; shuffle(d+1,d+n+1,rnd); solve(A,1); solve(B,2); puts("Yes"); for(int i=1;i<=n;i++) putchar(ch[ans[i]]); putchar('\n'); return 0; }
- ABC240Ex Sequence of Substrings
*3184
题目大意:给定一个长度为 n 的 01 串,求最多可以选出多少互不相交的子串,满足这些子串按照原串中的顺序,字典序严格升序。
n≤25000
虚高吧这玩意。
数据范围看上去就是根号老哥或者三只老哥的东西。
首先,我们选出来的子串,相邻的长度差一定不会大于 1。
于是我们容易发现直接过滤掉长度大于 √2n 的子串对答案没有影响。
那么像 Trie 那样直接 dfs 一遍,把所有长度在 √2n 之内的子串按字典序排好序,然后就是一个类似于最长上升子序列的问题,直接树状数组优化 dp 就好。时间复杂度 O(n√nlogn)。

#include<bits/stdc++.h> using namespace std; const int N=25005; const int sq=231; int n,m,a[N]; vector<int>o[2]; char str[N]; struct BIT{ int c[N]; #define lowbit(x) (x&(-x)) inline void update(int x,int y){ while(x<=n) c[x]=max(c[x],y),x+=lowbit(x); } inline int query(int x){ int res=0; while(x) res=max(res,c[x]),x-=lowbit(x); return res; } }T; struct Str{ int l,r; }b[N*sq]; inline void solve(vector<int>e,int len){ if(e.empty() || len*len>n*2) return; vector<int>v[2]; for(int i=0;i<e.size();i++){ b[++m]={e[i],e[i]+len-1}; if(e[i]+len-1<n) v[a[e[i]+len]].emplace_back(e[i]); } for(int i=0;i<2;i++) solve(v[i],len+1); } int main(){ scanf("%d%s",&n,str+1); for(int i=1;i<=n;i++) a[i]=str[i]^'0'; for(int i=n;i;i--) o[a[i]].emplace_back(i); solve(o[0],1); solve(o[1],1); for(int i=1;i<=m;i++){ int ri=T.query(b[i].l-1)+1; T.update(b[i].r,ri); } printf("%d\n",T.query(n)); return 0; }
- ABC155F Perils in Parallel
*2738
题目大意:数轴上有 n 盏电灯,每盏电灯有位置和状态( 0/1)两个属性。有 m 种操作,第 i 种操作可以将数轴上 [Li,Ri] 范围内的电灯状态取反。试构造操作方案使所有电灯状态为 0。
n≤105,m≤2×105,posi,Li,Ri≤109
强大题。
区间取反看上去好难受,要首先想到转化为异或差分。
然后每个操作可以看作连一条 (l,r+1) 的边,要选出一些边让每个点的度数的奇偶性与异或差分相同。
然后我们发现如果操作出现环那和没操作没区别,于是直接拉一个生成森林,DFS 求解即可。

#include<bits/stdc++.h> using namespace std; const int N=100005; inline int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x*f; } int n,m,c[N],d[N],vis[N],cnt,ans[N]; struct Edge{ int to,nxt,id; }e[N<<2]; int head[N],tot; inline void add(int u,int v,int i){ e[++tot]={v,head[u],i}; head[u]=tot; } struct BO_om{ int x,y; bool operator < (const BO_om &a) const { return x<a.x; } }a[N]; inline int dfs(int u){ int ret=d[u]; vis[u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(vis[v]) continue; if(dfs(v)) ret^=1,ans[++cnt]=e[i].id; } return ret; } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(); sort(a+1,a+n+1); for(int i=1;i<=n;i++) c[i]=a[i].x,d[i]=a[i].y^a[i-1].y; for(int i=1;i<=m;i++){ int rl=read(),rr=read(); int x=lower_bound(c+1,c+n+1,rl)-c,y=upper_bound(c+1,c+n+1,rr)-c; if(x==y) continue; add(x,y,i); add(y,x,i); } dfs(n+1); for(int i=1;i<=n;i++) if(!vis[i] && dfs(i)) return puts("-1"),0; sort(ans+1,ans+cnt+1); printf("%d\n",cnt); for(int i=1;i<=cnt;i++) printf("%d%c",ans[i],i==cnt?'\n':' '); return 0; }
- ARC121E Directed Tree
*2645
题目大意:
给定一棵 n 个点的有根树。
定义一个 1∼n 的排列 a 是合法的,当且仅当对于任意 i,不存在 ai→i 的,经过至少一条边的路径。
求合法排列数量。
n≤2000
和 ARC101E 做法很像,但我还是没做出来...
那个条件相当于 pi 不是 i 的祖先,于是考虑容斥,考虑这样的状态:设 fu,i,j 表示 u 的子树内,有 i 个点被我钦定选择当作祖先(即有后代连向它),然后这些关系连出来了 j 条链。容易发现的是,如果最后整个图在钦定完后有 k 条链,那么这个方案对答案就有 k! 的贡献。所以最后答案就是 ∑f1,i,j×j!。
但是直接这样转移是 O(n3)。类似于 ARC101E,将容斥的 −1 系数放在转移里面,就能够删掉第二维了,时间复杂度 O(n2)。

inline void dfs(int u,int fa){ siz[u]=1; f[u][0]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to;if(v==fa) continue; dfs(v,u); for(int j=0;j<=siz[u];j++) ff[j]=f[u][j],f[u][j]=0; for(int j=0;j<=siz[u];j++) for(int k=0;k<=siz[v];k++) (f[u][j+k]+=1ll*ff[j]*f[v][k]%mod)%=mod; siz[u]+=siz[v]; } for(int i=siz[u];~i;i--){ (f[u][i+1]+=f[u][i])%=mod; f[u][i]=-1ll*i*f[u][i]%mod; if(f[u][i]<0) f[u][i]+=mod; } }
- ARC063E Integers on a Tree
*2198
题目大意:一颗点权树,相邻节点点权差绝对值为 1。 现在一些点点权已确定,试构造一种方案。
n≤105
做法1:树形 dp 计算出每个数字能处于的范围,配合奇偶性判定答案可行性。水平太低,没写对。
做法2:考虑直接贪心,每次选一个确定的最小值然后拓展就行了... 为啥我这都想不到啊 /ll

int main(){ n=read(); for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v); add(v,u); } for(int i=1;i<=n;i++) val[i]=-111111; m=read(); for(int i=1;i<=m;i++){ int x=read(); val[x]=read(); c[val[x]].emplace_back(x); mx=max(mx,val[x]); } for(int i=0;i<=mx;i++) for(int j=0;j<c[i].size();j++){ int u=c[i][j]; for(int k=head[u];k;k=e[k].nxt){ int v=e[k].to; if(val[v] == val[u]-1 || val[v] == val[u]+1) continue; if(val[v]>-111111) return puts("No"),0; val[v]=val[u]+1; c[val[v]].emplace_back(v); mx=max(mx,val[v]); } } puts("Yes"); for(int i=1;i<=n;i++) printf("%d\n",val[i]); return 0; }
- ARC068F Solitaire
*3042
题目大意:将 1∼n 顺序加入双端队列,再依次弹出,求有多少种弹出序列,使得 1 是第 k 个被删的。
k≤n≤2000
怎么才能有思维啊 /kk
首先一定要找到正确的切入点:加入完的序列是单谷的,因此删除出来的东西应该可以看作 1 或 2 个下降序列,剩下的数则是谷左边或者右边的连续一段。
然而我想半个小时连这个都没想到 /qd
然后考虑 dp 计数这个东西。为避免两个当前删除序列的最小值是影响转移,不妨从大往小考虑是否删除一个数字,尝试设出这样的 dp 状态:设 fi,j 表示删了 i 个数,最小值为 j 的方案数。
令当前两个删除序列为 A,B,且 mina=j,minb>j。考虑新填进来一个数 k。
- 放到 A 里 则有 fi+1,k=fi+1,k+fi,j(k<j)
- 放到 B 里 则 k>j 且为未填进来的最大数字。 fi+1,j=fi+1,j+fi,j
大概照着转移,前缀和优化即可,注意转移边界,注意 k<n 时答案还有 2n−k−1 的系数,注意要特判 k=1。
(代码实现方式略有不同,但本质无差别)

for(int i=2;i<=n;i++) f[i]=1,g[i]=i-1; for(int i=2;i<k;i++){ for(int j=2;j<=n;j++) f[j]=(g[n]-g[j-(n-i+1>=j)]+mod)%mod; for(int j=2;j<=n;j++) g[j]=(g[j-1]+f[j])%mod; } ans=(k==1?1:g[n]); for(int i=1;i<=n-k-1;i++) (ans*=2)%=mod;
- AGC012D Colorful Balls
*2760
题目大意:
n 个球,每个都有颜色和重量两个属性。
对于两个同颜色的球,如果重量和在 x 以内可以交换位置。
对于两个不同颜色的球,如果重量和在 y 以内可以交换位置。
问可以得到的颜色序列的方案数。
n≤2×105
严重虚高。
考虑给能交换的球连边,容易发现联通块内部的点顺序可以自由排列。因此对于每个独立的联通块,答案就是 ∑cnti∏cnti!,整张图的答案就是所有联通块的答案乘起来。
直接建图是 O(n2) 的,但我们对于每个点,只有与最小的同色/异色球连边是有意义的。那么复杂度就是 O(nlogn)(瓶颈在排序)
这里只放连边部分的实现。

for(t=2;a[t].col==a[1].col;t++); fir[a[1].col]=1; for(int i=2;i<=n;i++){ if(!fir[a[i].col]) fir[a[i].col]=i; else if(a[i].w+a[fir[a[i].col]].w<=X) add(fir[a[i].col],i),add(i,fir[a[i].col]); if(a[1].col!=a[i].col){ if(a[i].w+a[1].w<=Y) add(1,i),add(i,1); } else if(t<=n && a[i].w+a[t].w<=Y) add(t,i),add(i,t); }
- AGC008F Black Radius
*3995
题目大意:给出一棵有 n 个结点的树 T={V,E} 和 V 的一个子集 U。定义一个结点的集合 S 合法当且仅当 S 能表示为 {y | y∈V,dis(x,y)≤d} 的形式,其中 x∈U,0≤d<n。求一共有多少个合法的集合。
n≤105
子任务:U=V。
捏麻,羊了以后啥都干不动,这个题从羊之前整到羊之后 /fn
先考虑 Subtask 是什么。
注意接下来的内容不计算整个树的情况,这种情况单独记,因为去掉这种情况后没有顶到上界,会有很多优秀的性质。
我们首先寻找一种精妙的对 S(x,d) 不重不漏地计数的方式:在 d 最小的位置计数。对于一种方案,在 d 最小时对应的 x 必然只有一种,可以考虑反证。
然后我们发现对于每个 x,可取的 d 是一个有上界的东西。
- 不覆盖全树
d<mxdis(x)
- 不存在 f(y,d−1)=f(x)
d−2<secdis(x)
理由可以自己画下,这个不是很难。于是换根 dp 即可,可以得到 Subtask 的分数。

// Subtask inline void dfs1(int u,int fa){ for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa) continue; dfs1(v,u); if(mx1[v]+1>mx1[u]) mx2[u]=mx1[u],mx1[u]=mx1[v]+1; else if(mx1[v]+1>mx2[u]) mx2[u]=mx1[v]+1; } } inline void dfs2(int u,int fa){ // d < mx1 && d-2 < mx2 ans+=min(mx1[u],mx2[u]+2); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa) continue; int l=(mx1[u]==mx1[v]+1)?(mx2[u]+1):(mx1[u]+1); if(l>mx1[v]) mx2[v]=mx1[v],mx1[v]=l; else if(l>mx2[v]) mx2[v]=l; dfs2(v,u); } }
- AGC030B Tree Burning
*2342
题目大意:一个周长为 L 的圆,上面有 L 个整点 0,1,⋯,L−1。有 n 个整点上有树,保证每个整点上最多有一棵树。现在你从 0 点出发,每次选择走到顺时针或逆时针方向的第一棵树,然后将它点燃。问点燃 n 棵树能走过的最长距离。
n≤2×105,L≤109
第一感就是说我顺逆时针交叉走会比较优,但是样例一似乎就把这个想法叉掉了。
但事实上我们稍加限制即可:在第一次改变方向之后,顺逆时针交叉走必然最优。证明可以考虑如果你走出了形如 LRR 这样的情况,我必然可以通过调整使它更优(且终点一致),如 RLR。
然后就用一个前后缀和递推计算即可,计算时要注意到达最后一个位置之后不需要返回原点。

int main(){ for(int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i]*2; for(int i=n;i;i--) suf[i]=suf[i+1]+(L-a[i])*2; for(int i=1;i<=n;i++){ int mid=(i+n)/2; ans=max(ans,pre[mid]-pre[i-1]+suf[mid+1]-((i+n&1)?L-a[mid+1]:a[mid])); } for(int i=n;i;i--){ int mid=(1+i+1)/2; ans=max(ans,suf[mid]-suf[i+1]+pre[mid-1]-((1+i&1)?a[mid-1]:L-a[mid])); } return 0; }
- CF17-Final H Poor Penguin
*3988
题目大意:
在遥远的南极洲, 有一片冰山。
这片冰山的状态可以用一个 n×m 的 01 矩阵表示, 其中 0 代表该位置是一块浮冰,1 代表该位置是一块冰山,矩形外的任何地方都是海洋。
我们称一块位于 (x,y) 的浮冰是稳定的, 当且仅当满足下列条件的至少一个:
- (x−1,y),(x+1,y) 均不为海洋。
- (x,y−1),(x,y+1) 均不为海洋。
任意时刻,若一块浮冰是不稳定的,则其会变为海洋,可以发现,具体变化的顺序不会影响最终 的答案。
现在在 (a,b) 的位置有一只企鹅, 保证这个位置是一块浮冰,随着气温升高,冰山在慢慢融化成 为浮冰,企鹅想知道,至少需要有多少个位置的冰山融化成为浮冰才能使得 (a,b) 这个位置最终变为海洋?
n,m≤40
这评分其实虚高了不少吧qwq,感觉整个题的思路都还算自然。
样例确实有非常强的启示作用。
首先我们不管这个神秘的数据范围,毛估估一下,海洋出现的过程显然是从四个角落往中间跑的。
那么我们看上去就有一个答案的上界:以 (a,b) 为中心切出来的四块矩形(包括边界)的冰山数量。
事实上,不构造数据直接随机的话,绝大多数答案都是这个。在 AT 上,它也过掉了近 30 个点(即使它样例 2 都没过)
那我们考虑啥时候答案会更优,手玩一下样例 2,发现它从两个角出发,将整个矩形切开了:
如果不考虑这种情况,上面的上界就是最优解。
然后你发现这相当于把大矩形挖掉两块,变成两个小矩形的子问题。
于是我们怎么暴力怎么来,直接设 fi,j,k,l 表示要把 (i,j) 为左上角,(k,l) 为右下角的矩形分出来需要的最小代价,转移时暴力枚举一个中间的切点,然后计算切掉两个角的贡献转移。最后统计答案直接枚举一个包含了 (a,b) 的矩形,用那个上界算最后一部分的贡献即可。复杂度是优秀的 O(n6) /cf,反正写的不是太劣应该随便过。

#include<bits/stdc++.h> #define y1 DitaMirika using namespace std; const int N=45; inline int rd(){ char ch=getchar(); while(ch!='P' && ch!='+' && ch!='#') ch=getchar(); return ch=='P'?2:ch=='#'; } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ a[i][j]=rd(); if(a[i][j]==2) X=i,Y=j,a[i][j]=0; c[i][j]=c[i-1][j]+c[i][j-1]-c[i-1][j-1]+a[i][j]; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=n;k>=i;k--) for(int l=m;l>=j;l--) f[i][j][k][l]=inf; f[1][1][n][m]=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=n;k>=i;k--) for(int l=m;l>=j;l--) for(int x=i;x<=k;x++) for(int y=j;y<=l;y++){ f[i][j][x][y]=min(f[i][j][x][y],f[i][j][k][l]+query(i,y+1,x,l)+query(x+1,j,k,y)); f[i][y][x][l]=min(f[i][y][x][l],f[i][j][k][l]+query(i,j,x,y-1)+query(x+1,y,k,l)); f[x][j][k][y]=min(f[x][j][k][y],f[i][j][k][l]+query(i,j,x-1,y)+query(x,y+1,k,l)); f[x][y][k][l]=min(f[x][y][k][l],f[i][j][k][l]+query(i,y,x-1,l)+query(x,j,k,y-1)); } ans=inf; for(int i=1;i<=1;i++) for(int j=1;j<=1;j++) for(int k=n;k<=n;k++) for(int l=m;l<=m;l++) ans=min(ans,f[i][j][k][l]+min(query(i,j,X,Y),min(query(i,Y,X,l),min(query(X,j,k,Y),query(X,Y,k,l))))); printf("%d\n",ans); return 0; }
ARC114C Sequence Scores
*2056
简单题,但我做麻烦了。
先考虑一下我们的操作过程应该长什么样。
显然我们会按 v 从小到大操作,这样一定是不劣的。
进一步的,我们对于 x 的相邻出现位置,即 ai=aj=x(i<j) 且 ∀k∈[i+1,j−1] ak≠x,如果 min{ai,⋯,aj}<x,则 ai,aj 必须分两次操作赋值,对答案有 1 的贡献。
记一个序列所有相邻出现位置统计得到的贡献为 cntA,所有数字的种类数为 cntB,那么答案即为 cntA+cntB。
cntB 不难算,直接 dp(设 fi,j 表示序列长度为 i,有 j 种不同数字的方案数) 或者容斥都不难做到 O(nm)。接下来考虑计数 cntA。
然后来设计一个暴力计数的方法:枚举左右端点 L,R 以及值 aL=aR=x,我需要在中间放至少一个 <x 的数以及若干 >x 的数(注意不能 =x,因为我需要保证 ∀k∈[L+1,R−1] ak≠x),其他位置我们并不关心。容斥即可做到 O(n3m),推出来式子即:
cntA=∑nL=1∑nR=L+1∑mx=1∑R−L−1i=1(−1)i−1×(R−L−1i)×(x−1)i×(m−1)R−L−1−i×mn−(R−L+1)
其中 i 为钦定 <x 的数个数。
考虑优化这个式子,首先我们发现枚举 L,R 的具体位置意义是不大的,在计算后面的式子时我们只关心 R−L−1 的值。这样前面改成枚举 len=R−L−1,算出来的贡献再乘上 n−len−1(即对应左端点 L 的取值个数),这样就是 O(n2m) 的了。
∑n−2len=1∑leni=1∑mx=1(−1)i−1×(leni)×(x−1)i×(m−1)len−i×mn−len−2
然后我们又发现式子里与 x 唯一相关的项即为 (x−1)i,那么对于相同的 i 这个式子的总系数就是 ∑m−1x=1xi,预处理一下即可去掉枚举 x 的过程,总复杂度降至 O(nm+n2),可以通过本题。
我偷懒直接用了快速幂计算,复杂度为 O(n2logn),1995ms 极限通过。

n=read(),m=read(); Init(max(n,m)); f[0][0]=1; for(int i=1;i<=n;i++) for(int j=1;j<=min(i,m);j++) f[i][j]=(1ll*f[i-1][j]*j+f[i-1][j-1])%mod; for(int i=1;i<=min(n,m);i++) (ans+=1ll*C(m,i)%mod*fac[i]%mod*f[n][i]%mod*i%mod)%=mod; for(int i=1;i<=n;i++) for(int val=2;val<=m;val++) (sumpw[i]+=qpow(val-1,i))%=mod; for(int len=1;len<=n-2;len++){ int ff=0; for(int i=1;i<=len;i++){ int rr=1ll*C(len,i)*sumpw[i]%mod*qpow(m-1,len-i)%mod*qpow(m,n-2-len)%mod; if(i&1) (ff+=rr)%=mod; else (ff+=mod-rr)%=mod; } (ans+=1ll*(n-len-1)*ff%mod)%=mod; } printf("%d\n",ans);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】