NOIP%2022.11
方阵
给定一个 n×n 方阵 a[i][j],和长度为 n 的数组 w[i]。
设 f[i] 为第 i 行的第 k[i] 小的区间 mex,求 max{f[i]+w[i]}。
n ≤ 10000
“第 k 小”提示了一种比较直接的做法——二分答案。对于每一行二分第 k 小的 mex,那么枚举区间右端点,左端点到它就要包含 0 ~ mid 中的所有数,可以使用并查集维护最右的左端点,统计区间个数即可。
我们发现这个做法是 的,无限接近 的正解了,如何优化掉这个 log 呢?
我们发现其实 mex 总共就 O(n) 种,可以直接一一试探,使用上面的方法 O(n) check。具体来说,先把行按照 w 从大到小排序,维护一个 ans,每次判断 ans+1-w[i] 是否合法,合法则 ans++ 即可。
早该砍砍了
n 的排列 p 上的一次操作为选择一个区间,并把区间内所有数变成它们的 min,问任意次操作(possibly zero)能够产生多少种不同的序列。
我们可以设 f[i] 表示前缀 i 的方案数,f[0]=1。对于 p[i],它能够作用到的范围是前面第一个小于到后面第一个小于的开区间,推导可知 for(int j=l;j<=r;j++)f[j]+=f[j-1];
,f[n] 就是答案。
棋盘染色
的棋盘初始全为 0,每次操作可以用 w[x][y] 的代价将 (x,y) 这个位置变成 1。执行完所有操作后,如果存在一个子矩阵的三个角都是 1,那么会自动免费把第四个角染成 1,直到不存在这样的子矩阵为止。问最终棋盘全变 1 所需最小总代价。
- 关键信息是“如果存在一个子矩阵的三个角都是 1,那么可以自动免费把第四个角染成 1”,很想知道“可以通过第二个操作把棋盘全部变成 1”这句话背后到底藏着什么
- 以点为对象的二维染色问题,不难使用经典套路——行列二分图
把两者有机结合一下,可以发现充要条件是图联通!于是变成了最小生成树问题,可用桶排代替 sort 做到 。
数三角
一个边被黑白染色的完全图中有多少个同色三角形?
正难则反,统计异色三角形个数。必然是一种颜色有一次、另一种颜色两次,我们如果能在黑白边交点处统计到这个三角形,只需要把答案除以 2 就可以输出了。
二择
有一个点数是 3×n,边数是 m 的无向图。称一个点集是 1 的,当且仅当其间没有连边;称一个边集是 2 的,当且仅当没有两条边有公共顶点。你需要找到一个大小为 n 的 1 的点集或一个大小为 n 的 2 的边集,或输出无解。
3×n 看起来很蹊跷;我们需要一个切入点,也就是翻译一下题意。1 不好翻译,2 就是一个【匹配】,当我们在图中描绘出一个最大匹配时,就会知道剩下的点都是满足“其间没有连边”的。原来 n 条边的 2 边集是包含 2n 个点的,如果不足 n 条边则剩下的点集是至少 n 个点的。
数塔
赛时许多人通过只进行 10 层递推操作,成功水过此题,数据太水强烈谴责
其实看到中位数我是敏感地想到了二分答案变成 01 之类的东西,但是不知道进一步该怎么做,感觉跟没转化前无别。
事实上就差一步了:通过手推几组数据找规律可以发现,最终最上面的数恰恰就是离中线最近的同色相邻对的颜色。特判一下不存在同色相邻对的情况(一直是黑白相间)
Camel and Oases
重复
一个字符串的一种剖分为将它分割成 6 个非空段,依次叫做 ,当它们形如 114514(即 )时是一种合法剖分。求仅包含 0 ~ 9 的字符串 的所有子串的合法剖分数之和。
又是一道 114514 题。它跟上一道 114514 题没有什么相似性,而且那个题是关于子序列,这题 abcdef 要一个连一个,所以那题的思路没什么可借鉴的。
观察 114514,这个 14514 比较的有趣,因为 14 和 14 是一样的,这个不是回文对吧,想想看这个叫什么来着,哦对,border。诶那不是 KMP 吗?KMP 可不可以做啊?好像可以,固定左端点,往右枚举串的右端点,就能知道每一个 border 的信息;预处理出左端点作为中介的 11 有多少个(),做个前缀和,就可以每次 O(1) 查询增量,做完了。
公交
一个 个点的数,当它以 为根时,你需要选择 条以 为一端的路径,包含于至少一条路径上的点被称为可用的点,每个点到距离它最近的可用点的距离乘以它的点权为它的代价,要求最小化所有点的总代价。对于 都输出答案。
考虑一条路径带来的影响。如果乍看看不出来的话,考虑以一定的顺序观察,这样往往能够发现变化量的规律。换句话说就是从根开始依次加入路径上的点,对答案的影响是什么?依次加入到当前点,恩泽所及之点便是它的子树之中的点,根据定义,答案的减少量应该就是子树中的权值和。因此一条路径的代价为路径上所有点的子树中点权和之和(重复经过的点不算)。这个就跟 accoders 上一道题比较像,是个长链剖分贪心可以解决的问题,这里略过。
考虑如何换根。换根就是要看换根前后哪些点的信息受到了影响,影响了多少。经过仔细分析不难发现受到影响的应该最多有 2 个点,因此可以暴力地维护一个 set 来表示每条长链的价值(这样可能稍有别扭,可以考虑开两个 set,一个维护前 k 大,一个存其余)。
乘筛积
给定 , 次询问 ,要求回答 。
题解做法:根号分治。
首先 exgcd 求出一组特解和两者的公差,然后暴力模拟统计答案。这是一个暴力的思路,我们考虑使用根号分治优化。
对 根号分治,如果小于根号,由于公差分别小于 ,这部分总复杂度为
而如果大于根号,虽然公差不一定大于根号,但也都接近于大于根号,感性理解 不会增加复杂度,所以总复杂度其实是 的,前面的部分可以通过对 p,q 记忆化完成。
放进去
给 个位置染色,共有 种可供选择的颜色。如果第 种颜色被使用过,那么需要支付 的代价;如果第 个位置染成了第 种颜色,需要支付 的代价。最小化总代价。
首先这个 为什么是 会吸引我们的眼球,细想会发现是个状压提示,这样一来,一个直观的认识是如果我们选择了子集 内的颜色,那么第二方面的总代价应该是对于所有位置,选择 内的该位置的 最小的那个。但是我们同样可以直观地发现要把 个最小值相加是一件难以快速实现的事情——由于这个“各位置对应min”的限制,我们不得不去枚举每个位置,从而导致复杂度超标;而如果能够把 min 也变成 ∑ 的形式,就会非常好。
【Trick】选择若干个元素 所选元素的权值中最小的那个是多少,这个问题可以转化为:对于每一个完整包含所选元素集的后缀(按照权值从小到大排序之后),有一个 的权值,那么由于包含它的后缀的左端点是一个前缀,我们用高维前缀和做一个超集前缀和就可以了。
对于这题而言,最后还需要对于每个 mask 把所得的超集权值和加上所选颜色的 ∑b,由于此题卡常,就用 的递推求这才能过。
最长路径
先吐槽一下其卡常和码农。
我觉得题解写得很好,你可以把它换一下
评一下,【套路】并查集可以用来线性(忽略 )解决多次的区间覆盖问题,原理是离线并按照覆盖优先级排序(例如,越靠后的覆盖越优先、本题中就是 越大的越优先,等),那么优先覆盖的位置从今往后都不更改了,删掉这个位置,可以看成若干个连续段,没个连续段有且只有右端点处是没删掉的,通过并查集维护 find(i) 表示 i 所在的块的右端点,改完就删即可。
复制//火车头就略了 #include <bits/stdc++.h> #define rsz(d) d[i].resize(m+1) using namespace std; inline int read(){ register char ch=getchar();register int x=0; while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void print(int x){ if(x/10)print(x/10); putchar(x%10+48); } const int N=2005,kQ=5e5+5,mod=1e9+7; int n,m,q,Q0[N],ql1[N],qr1[N],buc0[N],d[N][N],r[N][N],f[N][N],g[N][N],g0[N][N],g1[N][N],fa[2][N][N],Q1[N][N],buc1[N][N]; inline void add(int &x,int y){(x+=y)>=mod&&(x-=mod);} inline void sub(int &x,int y){(x-=y)<0&&(x+=mod);} struct Rect{int r1,c1,r2,c2;}rc[kQ]; int find(int ty,int id,int x){return x==fa[ty][id][x]?x:fa[ty][id][x]=find(ty,id,fa[ty][id][x]);} void solve(){ n=read(),m=read(),q=read(); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++)d[i][j]=r[i][j]=f[i][j]=0,g[i][j]=g1[i][j]=g0[i][j]=1; } for(int i=1;i<=n;i++){ for(int j=1;j<=m+1;j++)fa[0][i][j]=j; } for(int i=1;i<=m;i++){ for(int j=1;j<=n+1;j++)fa[1][i][j]=j; for(int j=0;j<=min(n,m);j++)buc1[i][j]=0; } for(int o=1;o<=q;o++)rc[o].r1=read(),rc[o].c1=read(),rc[o].r2=read(),rc[o].c2=read(); sort(rc+1,rc+q+1,[](Rect a,Rect b){return a.c2>b.c2;}); for(int i=1;i<=q;i++){ if(rc[i].r1==rc[i].r2)continue; for(int j=find(1,rc[i].c1,rc[i].r1);j<rc[i].r2;j=find(1,rc[i].c1,j+1)) r[j][rc[i].c1]=rc[i].c2,fa[1][rc[i].c1][j]=j+1; } sort(rc+1,rc+q+1,[](Rect a,Rect b){return a.r2>b.r2;}); for(int i=1;i<=q;i++){ if(rc[i].c1==rc[i].c2)continue; for(int j=find(0,rc[i].r1,rc[i].c1);j<rc[i].c2;j=find(0,rc[i].r1,j+1)) d[rc[i].r1][j]=rc[i].r2,fa[0][rc[i].r1][j]=j+1; } for(int i=1;i<=n;i++)for(int j=2;j<=m;j++)r[i][j]=max(r[i][j],r[i][j-1]); for(int i=1;i<=m;i++)for(int j=2;j<=n;j++)d[j][i]=max(d[j][i],d[j-1][i]); int ans=-1,ans2=0; for(int i=1;i<=m;i++)ql1[i]=1,qr1[i]=0; for(int i=n;i;i--){ if(i<n){ int ql0=1,qr0=0; for(int j=0;j<=min(n,m);j++)buc0[j]=0; for(int j=m;j;j--){ while(ql0<=qr0&&Q0[ql0]>r[i][j])sub(buc0[f[i+1][Q0[ql0]]],g0[i+1][Q0[ql0]]),ql0++; if(j+2<=m&&j+2<=r[i][j]){ while(ql0<=qr0&&f[i+1][j+2]>f[i+1][Q0[qr0]]){ sub(buc0[f[i+1][Q0[qr0]]],g0[i+1][Q0[qr0]]); qr0--; } Q0[++qr0]=j+2,add(buc0[f[i+1][j+2]],g0[i+1][j+2]); } if(ql0<=qr0){ int sumg=buc0[f[i+1][Q0[ql0]]]; if(f[i][j]<f[i+1][Q0[ql0]]+1)f[i][j]=f[i+1][Q0[ql0]]+1,g[i][j]=sumg; else if(f[i][j]==f[i+1][Q0[ql0]]+1)add(g[i][j],sumg); } } } for(int j=1;j<=m;j++){ if(j<m){ while(ql1[j+1]<=qr1[j+1]&&Q1[j+1][ql1[j+1]]>d[i][j])sub(buc1[j+1][f[Q1[j+1][ql1[j+1]]][j+1]],g1[Q1[j+1][ql1[j+1]]][j+1]),ql1[j+1]++; if(ql1[j+1]<=qr1[j+1]){ int sumg=buc1[j+1][f[Q1[j+1][ql1[j+1]]][j+1]]; if(f[i][j]<f[Q1[j+1][ql1[j+1]]][j+1]+1)f[i][j]=f[Q1[j+1][ql1[j+1]]][j+1]+1,g[i][j]=sumg; else if(f[i][j]==f[Q1[j+1][ql1[j+1]]][j+1]+1)add(g[i][j],sumg); } } if(ans<f[i][j])ans=f[i][j],ans2=g[i][j]; else if(ans==f[i][j])add(ans2,g[i][j]); g0[i][j]=g1[i][j]=g[i][j]; while(ql1[j]<=qr1[j]&&f[Q1[j][qr1[j]]][j]<f[i][j]){sub(buc1[j][f[Q1[j][qr1[j]]][j]],g[Q1[j][qr1[j]]][j]); qr1[j]--; } Q1[j][++qr1[j]]=i,add(buc1[j][f[i][j]],g1[i][j]); } } print(ans+1),putchar(' '),print(ans2),putchar('\n'); } int main(){ freopen("path.in","r",stdin);freopen("path.out","w",stdout); int T=read(); while(T--)solve(); }
排序
Inversion Swapsort
冒泡排序交换相邻,本题交换逆序对,所以想办法让下标的逆序体现在值域上,这样就没有限制了,手推可发现,排列的逆上做冒泡排序是可达到目的的。
Xorum
#include <bits/stdc++.h> #define int long long using namespace std; struct node {int t,x,y;}; vector<node>vec; inline void st(int t,int x,int y){vec.push_back(node{t,x,y});} int work(int x){ int bit=0; for(int i=20;~i;i--)if(x>>i&1){bit=i;break;} int y=x; while(bit--)st(1,y,y),y<<=1; st(2,x,y); int z=x^y; st(1,y,z); int p=z+y; st(2,x,p); int q=x^p; st(1,y,y); int o=y+y; st(2,o,q); int h=o^q,tmp=h>>1; int yy=y-tmp; while(yy){ if((yy&-yy)==h)st(2,h,y),yy-=h,y-=h; st(1,h,h),h<<=1; } st(2,tmp,x); return tmp^x; } signed main(){ int x; cin>>x; while(x>1)x=work(x); cout<<vec.size()<<'\n';for(auto p:vec)if(p.t==1)printf("%lld + %lld\n",p.x,p.y); else printf("%lld ^ %lld\n",p.x,p.y); }
Intresting Sections
https://www.luogu.com.cn/problem/CF1609F
【Trick】数区间问题,只有两种方法:(1)分治(2)固定左端点扫右端点
本题中我们采用前者。现在求出 跨 的答案。
从 到 枚举区间的左端点 ,右端点 的三种不同取值范围对应了三种不同统计方法
- :一个满足要求的区间的 max 和 min 都在左半部分取到,只需预处理左半部分的每个后缀的 max 和 min 的 popcount 相等与否。
- :一个满足要求的区间的 max 和 min 二选一在左半部分取到,另一个在右半部分取到,由于此时已经知道 ,所以右边的 的 popcount 也知道了,只需查询一个区间内前缀 min/max 的 popcount 为某一定值的点有几个,使用桶+差分做。
- :一个满足要求的区间的 max 和 min 都在右半部分取到,只需预处理出一个区间内前缀 min/max 相等的点的个数即可。
#include <bits/stdc++.h> #define int long long using namespace std; inline int read(){ register char ch=getchar();register int x=0; while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } const int N=1e6+5,INF=2e18; int n,ans,a[N],c3[N],mn2[N],mx2[N],bucmn[64],bucmx[64],arr[N],ppc[N]; map<int,int>mp; inline int Popc(int x){int cnt=0;while(x)cnt++,x-=x&-x;return cnt;} #define popc(x) ppc[x] void solve(int l,int r){ if(l==r){ ans++; return; } int mid=l+r>>1,p1=mid,p2=mid; mx2[mid]=-INF,mn2[mid]=INF; c3[mid]=0; for(int i=mid+1;i<=r;i++){ mn2[i]=min(mn2[i-1],a[i]),mx2[i]=max(mx2[i-1],a[i]); c3[i]=c3[i-1]+(popc(mx2[i])==popc(mn2[i])); } int mx=-INF,mn=INF; for(int i=mid;i>=l;i--){ mn=min(mn,a[i]),mx=max(mx,a[i]); while(p2<r&&(mn<=mn2[p2+1]||mx>=mx2[p2+1]))p2++,bucmn[popc(mn2[p2])]++,bucmx[popc(mx2[p2])]++; while(p1<p2&&mn<=mn2[p1+1]&&mx>=mx2[p1+1])p1++,bucmn[popc(mn2[p1])]--,bucmx[popc(mx2[p1])]--; if(popc(mn)==popc(mx))ans+=p1-mid; if(mn>mn2[p2])ans+=bucmn[popc(mx)]; else ans+=bucmx[popc(mn)]; ans+=c3[r]-c3[p2]; } while(p2>p1)bucmn[popc(mn2[p2])]--,bucmx[popc(mx2[p2])]--,p2--; solve(l,mid),solve(mid+1,r); } signed main(){ //freopen("interval.in","r",stdin);freopen("interval.out","w",stdout); n=read(); for(int i=1;i<=n;i++)a[i]=read(),arr[i]=a[i]; sort(arr+1,arr+n+1); int u=unique(arr+1,arr+n+1)-arr-1; for(int i=1;i<=u;i++)mp[arr[i]]=i; for(int i=1;i<=n;i++)a[i]=mp[a[i]]; for(int i=1;i<=u;i++)ppc[i]=Popc(arr[i]); solve(1,n); cout<<ans<<'\n'; }
Cleaning
https://www.luogu.com.cn/problem/AT_agc010_c
前置知识:套路题条件反射第 39 条(CF 上叫 Zero Sum)
首先还原出那个树上差分的数组 ,自下而上处理每个非叶子节点,这样遇上的时候它上面写着一个负数,它的绝对值就代表子树有多少条跨越它的路径,然后由于树上差分的过程,我们要把 ,到时才能得到真正的 。现在对于 ,它的每个儿子子树都会有一些过剩的叶子上的值,这里就变成了“前置知识”中的问题,求最后最少剩几个(我们一定希望剩得越少越好,因为如果少的不行,多的也肯定不行)。最终看一下剩不剩就行了。注意在途中判断 c 是否为负值。
机器战警
由于机器人的行走实质上是坐标的变化,我们可以令 L = -1, R = 1。对于一个操作子串 [l,r],若存在一个前缀和 S(l,p)>n-x,在 x 处出发执行 [l,r] 操作序列必然爆炸;否则,如果其最大子段和 ≥ n,也必然爆炸;否则不爆炸。
于是,对于每一个最大子段和 ≤ n - 1 的子串 [l,r],x_max = n - 最大前缀和。
这可以通过倒序枚举左端点并控制右端点的指针,并维护 [l,r] 内前缀和的单调队列来办到。具体见代码。
#include <bits/stdc++.h> using namespace std; inline char readc(){ register char ch=getchar(); while(ch!='L'&&ch!='R')ch=getchar(); return ch; } const int N=1e7+5,mod=1e9+7; int n,m,ql,qr,tp,a[N],pre[N],Q[N],stk[N],suc[N]; inline void add(int &x,int y){(x+=y)>=mod&&(x-=mod);} int main(){ cin>>n>>m; for(int i=1;i<=m;i++)a[i]=(readc()=='L'?-1:1); for(int i=1;i<=m;i++)pre[i]=pre[i-1]+a[i]; for(int i=m;i;i--){ while(tp&&pre[stk[tp]]<=pre[i])tp--; if(tp)suc[i]=stk[tp];else suc[i]=m+1; stk[++tp]=i; } ql=m+1,qr=m; int ans=0,cnt=0,sum=0; for(int i=m,j=m;i;i--){ while(ql<=qr&&pre[Q[ql]]<pre[i])ql++; Q[--ql]=i; while(j>=i&&(ql<=qr&&pre[Q[qr]]-pre[i-1]>n-1)){ sum-=pre[Q[qr]],sum%=mod,(sum+=mod)%=mod; if(ql<=qr&&Q[qr]==j)qr--; j--; } if(i<=j)sum+=pre[i],sum%=mod,(sum+=mod)%=mod; (cnt+=(j-i+1ll)*((n+pre[i-1]+mod)%mod)%mod)%=mod; for(int k=i+1;k<=min(j,suc[i]-1);k=suc[k])add(sum,1ll*(pre[i]-pre[k]+mod)*(min(min(j,suc[k]-1),suc[i]-1)-k+1)%mod); //(2+2)+(2+1)+(2+2)*2+(2+1)*3+(2)=4+3+8+9+2=26 add(ans,(sum+(max(0ll,(long long)pre[i-1]-pre[i])+mod)%mod*(min(suc[i]-1,j)-i+1))%mod); } cout<<(cnt-ans+mod)%mod; }
子串划分
对 的 种划分,将每一段插入字典树,求字典树的大小之和。
划分字典树含于后缀字典树,先建出后缀字典树,考虑每个节点的贡献(1×在几种方案中存在过)。
每个节点对应一条从根到它的路径所代表的子串,这些子串是互异的(所以其实没必要显式建树,但是这样还是方便些)。我们找到这个子串在原串中的每一次出现的起始位置 ;它对一个方案有贡献当且仅当这个方案中有至少一个段从某个 开始,并完整地包含了它。“至少一个”启发我们容斥,但由于重重叠叠的位置关系的复杂性,我们考虑用 dp 的角度考虑,设 表示阶段到了 这个串,不存在任何一个段算过的方案数,。那么 就需要用 减去 这个串被包含且前面都没被包含过的方案数。后者就是 , 表示往前数第一个不跟 重叠的 串。原因在于重叠的肯定已经没被包含过(因为我们在 前面放了一个挡板)。特别注意当 时,只用 ,因为前面的挡板不用钦定,永远在那儿。
关于时间复杂度:我们应该用 的时间处理 的贡献,这样总复杂度就是所有互异子串的出现次数之和,等于总子串个数 。可以通过预处理来办到。
UER#11 切割冰片
经过少量的 observe 可以将问题转化为:初始时有一个集合 ,在 这 个时刻分别删除 这些数,要求在时刻 的集合中分别选一个数,使得形成的序列随着时间轴而不降,求方案数。
显然的 dp,转移就是做前缀和,删除 时就把对应的 删掉。这样就是 的,如果卡常的话可以获得 80 分,但是我不知道 5e8 是可以 1s 过的,所以没有卡常,只有 60 分。
每次转移就是对 dp 数组做前缀和的 dp 可以通过路径计数优化。对 进行 次前缀和运算,得到的 ,可 枚举 ,并计算 对 的贡献 。而总共只会进行 次删除,也就是 次进行这种 的批量前缀和转移。
#include <bits/stdc++.h> using namespace std; const int N=505,mod=998244353; int m,n,l[N],ord[N],tmp[N],C[N],bk[N],jc[N],inv[N]; vector<int>f; inline void add(int &x,int y){(x+=y)>=mod&&(x-=mod);} inline int qp(int a,int b){ int c=1;for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)c=1ll*c*a%mod;return c; } inline void Erase(int id){ for(vector<int>::iterator it=f.begin();it!=f.end();it++){ if(!id){f.erase(it);break;} id--; } } int main(){ scanf("%d%d",&m,&n); for(int i=1;i<=n;i++)scanf("%d",&l[i]),l[i]++,ord[i]=i; for(int i=1;i<=n+1;i++)inv[i]=qp(i,mod-2); sort(ord+1,ord+n+1,[&](int i,int j){return l[i]<l[j];}); f.resize(n+2); for(int i=1;i<=n+1;i++)f[i]=1; l[0]=1; int las=1; for(int i=1;i<=n;i++){ if(l[ord[i]]>m)break; for(int j=1;j<f.size();j++)tmp[j]=0; C[0]=1; for(int d=1;d<=n+1;d++){ C[d]=1ll*C[d-1]*(l[ord[i]]-l[ord[i-1]]-1+d)%mod*inv[d]%mod; } if(l[ord[i]]!=l[ord[i-1]]){ for(int j=1;j<f.size();j++)for(int k=j;k<f.size();k++){ add(tmp[k],1ll*f[j]*C[k-j]%mod); } } else { for(int j=1;j<f.size();j++)tmp[j]=f[j]; } for(int j=1;j<f.size();j++)f[j]=tmp[j]; bk[ord[i]]=1; int num=0; for(int j=1;j<=ord[i];j++)num+=bk[j]; Erase(ord[i]-num+1); las=l[ord[i]]; } for(int j=1;j<=n+1;j++)tmp[j]=0; C[0]=1; for(int d=1;d<=n+1;d++){ C[d]=1ll*C[d-1]*(m-las-1+d)%mod*inv[d]%mod; } if(m!=las){ for(int j=1;j<f.size();j++)for(int k=j;k<f.size();k++){ add(tmp[k],1ll*f[j]*C[k-j]%mod); } } else { for(int j=1;j<f.size();j++)tmp[j]=f[j]; } for(int j=1;j<f.size();j++)f[j]=tmp[j]; int ans=0; for(int i=1;i<f.size();i++)add(ans,f[i]); cout<<ans; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】