菜鸡wwb因为想不出口胡题所以来写题解了
A. chess
昨天晚上考试,有点困
开考先花五分钟扫了一边题,好开始肝$T1$
看了一眼$m$的范围很大,第一反应矩阵快速幂??
$n$很小,那么可以打$n^4$的DP,
$10min$过去了,好像就是一个$DP$啊,随便乘个组合数就好了,
最后距离考试$20min$时,因为瞎取模,把自己的$AC$覆盖了kukukuku
正解的话,首先对于第一列而言,第$1+n$列的放的$C$的个数与他相同
但是因为只知道数目我们乘上组合数就好
$f_{ij}$表示第$i$行,一共放了$j$个棋子的方案数,转移即可
思路积累:
1。快速幂要预处理
2。指数不能取模
3。观察数据范围合理进行递推
B. array
觉得题还是不错的考场想了半个小时想到的
题意:
给出一个序列,每个数有一个权值,求满足$a_{k}\leq a_{j}\leq a_{i}(k\leq j\leq i)$中$k$的最小值
我的做法是维护两个单调栈,一个单调递增,一个单调递减
假设当前单减的栈的只栈顶是$maxtop$,递减的是$mintop$;
对于单减的栈当我们插入$i$后,$st_{maxtop-1}$是左边第一个大于$i$的节点,
所以我们已经满足了第一个条件,
对于第二个条件因为在单增栈中对于任意节点$k$,从它到栈顶的所有值都大于它
因此我们通过$st_{maxtop-1}$直接在单增栈中$upperbound$即可
当然这是考场瞎打水过的,并不是正解。
事实上不用维护第二个栈只需要在每次弹递增栈时记录一个数组$pos_{j}$表示从$j$开始到左侧第一比他大的元素中
权值最小的元素位置,没一段$pos_{j}$都控制一段区域,可以在弹栈中维护。
思路积累:
1.对于区间具有单调性的问题可以考虑单调栈
2.单减的栈保证栈中每个元素到栈首的值都小于等于该元素
单增保证每个元素到栈首的值都大于等于该元素
C. ants
听说是原题,我又没做过......
$50$算法
莫队+线段树,维护区间最长连续区间和
$100$算法
回滚莫队+并查集
第一次接触回滚莫队,莫队大法吼....
简单叙述一下
对于一些问题我们发现对于区间的移动而言加\减的操作很难维护,那么为了比较好的时间复杂度,可以采用回滚莫队
对于该题,他的删数操作我们很难维护区间最大子段和,那么我们开始思考我们令操作中只有加操作即可
那么我们采用分块思想,区间左端点所在块为第一关键字,右端点为第二关键字
bool cmp(no a,no b){return (bel[a.l]==bel[b.l])?(a.r
然后我们选一个块,发现我们先处理出$L-r_{x}$的部分,
也就是说处理出左端点到他所在的块的最右端,然后我们发现对于右端点是单调的
那么我们保持右端点的贡献不清空,每次清空左段点到块的那部分
当然假如两端点在同一块内就直接暴力处理
至于并查集
采用按秩合并思想,然后我们用一个栈记录以前的相连的点,相当与是时间戳一样
然后每次回溯撤销
注意对于右边区间我们要继承上一状态
#include#define int long long #define MAXN 1100000 using namespace std; int read(){ char c=getchar();int x=0; while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x; } int deep[MAXN],fa[MAXN];int n,m;int a[MAXN]; struct node{ int fa,to; void insert(int x,int y){fa=x;to=y;} }T[MAXN];int top=0; void del(){ while(top){ fa[T[top].to]=T[top].to; deep[T[top].fa]-=deep[T[top].to]; top--; } } int ans[MAXN];int bel[MAXN];int kuan;int l[MAXN],r[MAXN]; struct no{int l,r,id;}e[MAXN]; int find(int x){ if(fa[x]==0)return 0; if(fa[x]==x)return x; return find(fa[x]); } bool cmp(no a,no b){return (bel[a.l]==bel[b.l])?(a.r b.l);} void init(){ memset(l,0x3f3f3f,sizeof(l)); kuan=sqrt(n); for(int i=1;i<=n;++i){ int me=(i-1)/kuan+1; bel[i]=me; l[me]=min(i,l[me]); r[me]=max(i,r[me]); } } vector<int>v[MAXN]; int merge(int x,int y,int opt){ if(deep[x]>=deep[y]){ deep[x]+=deep[y]; fa[y]=x; if(opt)T[++top].insert(x,y); return deep[x]; } else{ deep[y]+=deep[x]; fa[x]=y; if(opt)T[++top].insert(y,x); return deep[y]; } } void work(int x){ int R=r[x];//printf("x=%lld\n",x); int rs=0; for(int k=0;k k){ int to=v[x][k]; int ll=e[to].l;int rr=e[to].r; //printf("ll=%lld rr=%lld be=%lld ber=%lld\n",ll,rr,bel[ll],bel[rr]); if(bel[ll]==bel[rr]){ int maxn=0; for(int i=ll;i<=rr;++i){ fa[a[i]]=a[i];deep[a[i]]=1;maxn=max(deep[a[i]],maxn); int fa1=find(a[i]-1); if(fa1){maxn=max(merge(fa1,a[i],1),maxn);} int fa2=find(a[i]+1);int me=find(a[i]); if(fa2){maxn=max(merge(fa2,me,1),maxn);} } ans[e[to].id]=maxn; del(); for(int i=ll;i<=rr;++i)fa[a[i]]=0,deep[a[i]]=0; } else{ int maxn=0; for(int i=R+1;i<=rr;++i){ //printf("work2 a[%lld]=%lld\n",i,a[i]); fa[a[i]]=a[i];deep[a[i]]=1;maxn=max(deep[a[i]],maxn); int fa1=find(a[i]-1); if(fa1){maxn=max(merge(fa1,a[i],0),maxn);rs=max(maxn,rs);} int fa2=find(a[i]+1);int me=find(a[i]); if(fa2){maxn=max(merge(fa2,me,0),maxn);rs=max(maxn,rs);} } for(int i=ll;i<=r[x];++i){ //printf("work1 a[%lld]=%lld\n",i,a[i]); fa[a[i]]=a[i];deep[a[i]]=1;maxn=max(deep[a[i]],maxn); int fa1=find(a[i]-1); if(fa1){maxn=max(merge(fa1,a[i],1),maxn);} int fa2=find(a[i]+1);int me=find(a[i]); if(fa2){maxn=max(merge(fa2,me,1),maxn);} } ans[e[to].id]=max(maxn,rs); del(); R=rr; for(int i=ll;i<=r[x];++i)fa[a[i]]=0,deep[a[i]]=0; } } for(int i=r[x]+1;i<=R;++i)fa[a[i]]=0,deep[a[i]]=0; } signed main(){ //freopen("1.in","r",stdin); //freopen("w.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;++i){ a[i]=read(); } init(); for(int i=1;i<=m;++i){ e[i].l=read();e[i].r=read();e[i].id=i; } sort(e+1,e+m+1,cmp); for(int i=1;i<=m;++i){int to=e[i].l;v[bel[to]].push_back(i);} for(int i=1;i<=(n-1)/kuan+1;++i){ if(!v[i].size())continue; work(i); } for(int i=1;i<=m;++i){ printf("%lld\n",ans[i]); } }
思路积累
1.回滚莫队处理比较难的区间操作
2.查询区间连续子段长度,可以按秩合并并查集