今天的题,除了T2感觉都不是我可做的啊,这真“CSP”呢
(算了算了等我以后变强了再来做吧,先丢在这)
还是决定补补骗分法:
T1:直接杨辉三角预处理出组合数,然后计算即可。注意:杨辉三角和实际组合数并不是一一对应,而是正好横纵坐标相反,且注意
要+1,即整体下移左移一位
T2:(一开始读错题了)对于每一个
和
连弦的,直接找到询问区间里较小的那个数然后
即可
T3:打表,注意旋转同构,但轴对称是两种不同方案即可
T1
sol:
T2
sol:
思路:
1. 开一个栈来记录当前的节点,如果放进栈中的是一个右节点,那么就一直弹栈直到弹到合法的左节点为止,每次弹栈处理当前区间的
,相当于合并当前合法块为一个区间(此时是最小的合法区间),方便以后对每个合法区间建一棵树
2. 如果当前区间的右端点就是当前区间,说明它已经在上一步被处理成了一个合法区间,于是将这个点的父亲标记为前面那个区间的左端点的前一位(相当于合并现在这两个区间,两个合法区间的交集一定是合法的),记录一下节点那条边相连的点
3. 如果当前区间还没有被建树并且当前区间是一个合法区间,对这个区间建树
4. 处理询问,对于每一组询问,判断是否合法,输出它们
的深度
即可
#include <bits/stdc++.h>
using namespace std;
const int N=(int)2e6+5;
int n,m;
int l[N],r[N],f[N][20],dep[N],sta[N],top,fa[N],rt[N],r1,r2,p[N];
vector<int> v[N];
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c&15);c=getchar();}
return cnt*f;
}
void dfs(int u,int RT){
rt[u]=RT;
for(int i=1;i<=18;i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=0;i<v[u].size();i++){int to=v[u][i];dep[to]=dep[u]+1;f[to][0]=u;dfs(to,RT);}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;i>=0;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--)
if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){
n=read(),m=read();
for(int i=1;i<=(n<<1);i++)p[i]=read();
for(int i=1;i<=(n<<1);i++){
l[i]=min(i,p[i]);r[i]=max(i,p[i]);
while(top&&l[i]<=sta[top]){
int x=sta[top--];
l[i]=min(l[i],l[x]);r[i]=max(r[i],r[x]);
}sta[++top]=i;
}
memset(fa,-1,sizeof(fa));memset(rt,-1,sizeof(rt));
for(int i=1;i<=(n<<1);i++){if(r[i]==i){fa[i]=l[i]-1;v[fa[i]].push_back(i);}}
for(int i=0;i<=(n<<1);i++){if((!(~rt[i]))&&(v[i].size())){dep[i]=1;dfs(i,i);}}
for(int i=1;i<=m;i++){
r1=read(),r2=read();
if(r1>r2)swap(r1,r2);
if(!r1){puts("0");continue;}
if(!(~rt[r1])||!(~rt[r2])){puts("0");continue;}
if(rt[r1]^rt[r2]){puts("0");continue;}
cout<<dep[lca(r1,r2)]-1<<'\n';
}
return 0;
}