——亚斯娜《刀剑神域》
1 #include<bits/stdc++.h> 2 #define mod 1000000007LL 3 #define int long long 4 #define LL long long 5 using namespace std; 6 LL fac[105],inv[105],f[105][10005],tmp[2][10005]; 7 LL n,m,c,Ans; 8 LL mgml(LL a,LL b,LL ans=1){ 9 for(;b;b>>=1,a=a*a%mod) if(b&1) ans=ans*a%mod; 10 return ans; 11 } 12 LL C(int x,int y){ 13 if(x<y) return 0; 14 return fac[x]*inv[y]%mod*inv[x-y]%mod; 15 } 16 signed main(){ 17 fac[0]=inv[0]=inv[1]=1; 18 for(int i=1;i<=100;++i) fac[i]=fac[i-1]*i%mod; 19 for(int i=2;i<=100;++i) inv[i]=inv[mod%i]*(mod-mod/i)%mod; 20 for(int i=2;i<=100;++i) inv[i]=inv[i-1]*inv[i]%mod; 21 scanf("%lld%lld%lld",&n,&m,&c); 22 const LL t1=m/n,t2=m%n; 23 for(int i=0;i<=n;++i) tmp[0][i]=mgml(C(n,i),t1),tmp[1][i]=mgml(C(n,i),t1+1); 24 f[0][0]=1; 25 for(int i=1;i<=n;++i) 26 for(int j=0;j<=min(c,n*i);++j) 27 for(int k=0;k<=min(n,j);++k) 28 f[i][j]=(f[i][j]+f[i-1][j-k]*(i<=t2?tmp[1][k]:tmp[0][k])%mod)%mod; 29 printf("%lld\n",f[n][c]); 30 return 0; 31 }T1
1 #include<bits/stdc++.h> 2 #define N 10000005 3 using namespace std; 4 inline int read(){ 5 register int x=0,f=1;char ch=getchar(); 6 while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar(); 7 while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-48,ch=getchar(); 8 return x*f; 9 } 10 int n,top,Ans; 11 int A[N],sta[N],nxt[N]; 12 int main(){ 13 n=read(); 14 for(int i=1;i<=n;++i) A[i]=read(),nxt[i]=i; 15 for(int i=1;i<=n;++i){ 16 while(top&&A[sta[top]]<=A[i]) (A[nxt[sta[top]]]<=A[nxt[i]])?nxt[i]=min(nxt[i],nxt[sta[top]]):0,top--; 17 sta[++top]=i; 18 } 19 for(int i=1;i<=n;++i) Ans=max(Ans,i-nxt[i]+1); 20 return !printf("%d\n",Ans); 21 }T2
1 #include<bits/stdc++.h> 2 #define N 100005 3 using namespace std; 4 inline int read(){ 5 register int x=0,f=1;char ch=getchar(); 6 while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar(); 7 while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-48,ch=getchar(); 8 return x*f; 9 } 10 int n,m,blosiz,blonum,top,ans; 11 int bel[N],ant[N],L[N],R[N],Ans[N]; 12 struct node{int l,r,id,ans;}sta[1200]; 13 struct Query{int l,r,id;}q[N]; 14 bool cmp(const Query &a,const Query &b){return bel[a.l]!=bel[b.l]?bel[a.l]<bel[b.l]:a.r<b.r;} 15 void Record(int pos){ 16 sta[++top]=(node){L[pos],R[pos],pos,ans}; 17 } 18 void Convolution(){ 19 while(top){ 20 L[sta[top].id]=sta[top].l; 21 R[sta[top].id]=sta[top].r; 22 ans=sta[top].ans; 23 top--; 24 } 25 } 26 int main(){ 27 // freopen("text.in","r",stdin); 28 // freopen("a.out","w",stdout); 29 n=read(),m=read(),blosiz=sqrt(n),blonum=ceil((double)n/blosiz); 30 for(int i=1;i<=blonum;++i) 31 for(int j=(i-1)*blosiz+1;j<=i*blosiz&&j<=n;++j) 32 bel[j]=i; 33 for(int i=1;i<=n;++i) ant[i]=read(); 34 for(int i=1;i<=m;++i) q[i].id=i,q[i].l=read(),q[i].r=read(); 35 sort(q+1,q+m+1,cmp);int hea=1; 36 for(int I=1;I<=blonum;++I){ 37 memset(L,0,sizeof L); 38 memset(R,0,sizeof R); 39 top=0; ans=1; 40 int r=I*blosiz; 41 while(hea<=m&&bel[q[hea].l]==I){ 42 // printf("在第 %d 块 L:%d R:%d\n",I,q[hea].l,q[hea].r); 43 if(bel[q[hea].r]==I){ 44 for(int j=q[hea].l;j<=q[hea].r;++j){ 45 int i=ant[j]; 46 Record(i); 47 if(!L[i]) L[i]=i,R[i]=i; 48 int MINL=L[i-1]==0?i:L[i-1],MAXR=R[i+1]==0?i:R[i+1]; 49 Record(MINL),R[MINL]=MAXR; 50 Record(MAXR),L[MAXR]=MINL; 51 ans=max(ans,MAXR-MINL+1); 52 } 53 Ans[q[hea].id]=ans; 54 Convolution(); 55 hea++; 56 continue; 57 } 58 while(q[hea].r>r){ 59 ++r;int i=ant[r]; 60 if(!L[i]) L[i]=i,R[i]=i; 61 int MINL=L[i-1]==0?i:L[i-1],MAXR=R[i+1]==0?i:R[i+1]; 62 R[MINL]=MAXR; 63 L[MAXR]=MINL; 64 ans=max(ans,MAXR-MINL+1); 65 } 66 for(int j=I*blosiz;j>=q[hea].l;--j){ 67 int i=ant[j]; 68 Record(i); 69 if(!L[i]) L[i]=i,R[i]=i; 70 int MINL=L[i-1]==0?i:L[i-1],MAXR=R[i+1]==0?i:R[i+1]; 71 Record(MINL),R[MINL]=MAXR; 72 Record(MAXR),L[MAXR]=MINL; 73 ans=max(ans,MAXR-MINL+1); 74 } 75 Ans[q[hea].id]=ans; 76 Convolution(); 77 ++hea; 78 } 79 } 80 for(int i=1;i<=m;++i) printf("%d\n",Ans[i]); 81 return 0; 82 }T3
A. chess
容易发现当你确定前n行时,后面的m-n行就已经确定了。
那么我们只需要通过dp确定前n行的摆放情况,然后乘以次数即可。
具体的,就是将m拆成$m/n$ 与 $m\%n$
dp转移是$$dp[i][j]=\ dp[i-1][j-k]* \ tmp[0/1][k]$$
对于[1,m%n]的情况,$tmp[1][k]=C(n,m/n+1)$
否则就是$tmp[0][k]=C(n,m/n)$
B. array
一眼看错题。
其实题目的意思是:选择一个k,满足所有[k,i]之间的j,都有$a[k]<=a[j]<=a[i]$
那么首先提炼出来我们要找i作为最大值的区间的最小值的最小位置对吧。
如果暴力找的话用单调栈+ST表维护复杂度$O(n*log\ n)$过不去的。
然后就是很神奇的算法优化了。
我们考虑单调栈寻找区间最大值的过程。
for(int i=1;i<=n;++i){ while(top&&A[sta[top]]<=A[i]) top--; sta[++top]=i; }
有没有方法使得最小值能够继承呢?仔细思考。
我们发现:对于当前i能被挤出去的点的最大值区间,一定属于i的最大值区间。这不显然吗
那么我们既然要找的是i的最大值区间的最小值(忘了的看上边),是不是可以从很多个它挤出去的区间中合并出一个答案呢?
顺着这个思路,我们慢慢解决了这个问题。
具体的,就是将每个点记录一个数组$Nxt$表示$ \(上一个栈里的元素,i]\ $之间的最小值的最小下标。
每个点的Nxt初始设为它自己。
实际上每个点最后的Nxt就是它的答案的$k$。
那么当i节点挤出top节点时,我们判断一下top节点的Nxt的值是否比i节点的Nxt值小于等于,如果是,就更新i节点的Nxt。
并且由于我们是正向枚举的,就保证了Nxt如果能更新,值和下标就一定单调不增。
代码也就加了一句话,非常简单。
for(int i=1;i<=n;++i){ while(top&&A[sta[top]]<=A[i]) (A[nxt[sta[top]]]<=A[nxt[i]])?nxt[i]=min(nxt[i],nxt[sta[top]]):0,top--; sta[++top]=i; }
但是比较难想,我想了很久。
不要盲从于题解,如果认为题解不够优秀(比如我)不妨大胆破旧。
还是要把思路打开。
C. ants
一眼莫队线段树,丝毫没有意识到这是一道原题。
原到什么程度呢,就是说把那道题的代码放到这里,直接AC
话说那道原题我还写过莫队线段树的题解...
现在就因为当时水题掉RP了吧...
正解:回滚莫队。
考虑对于这种添加元素好添加,删除不好删除的情况,莫队的复杂度会生一个档次。
尤其是我们的修改操作还不是$O(1)$的情况下。
那么不妨换个思路进行思考。
为什么我们要用线段树呢?因为添加删除都好搞。
那么如果我们没有了删除,有没有更好的方式来摆脱这个log呢?ZKW如果没有了删除,我们发现其实用并茶几/桶都可以轻松维护连续区间的最大长度。
考虑怎么删除删除操作。
将枚举询问改为枚举块,那么左端点同一个块内的右端点满足单不降。
那么右端点就可以不删除只添加了。考虑左端点。
因为它们都在同一个块内,因此直接暴力每次将这个块的最后到L的所有都添加,然后再记录之前的状态直接回去就好了。
复杂度:每个询问左端点至多移动$O( \sqrt{n})$次,右端点单增不影响复杂度。
总复杂度:$$O(\ n* \sqrt{n})$$
以后用奇迹银桥水过的题一定要看看正解啊qaq...