题解:P16019 [ICPC 2021 NAC] Permutation CFG
lailai0916 · · 题解
题意简述
排列中的符号
解题思路
记
先计算完整块的长度。每次展开都对前一层长度做一次关于
类似地,当
回答
为了快速定位子块,按数值建立可持久化线段树。第
在线段树每个结点中,对
它正是对应子树内所有
还需统计末尾块之前所有完整块的贡献。假设这些块的符号为
不必保存
这里上指标
其中
设末尾块位于排列位置
块长度只用于和询问前缀比较。超过 long long 安全上界的长度统一截断为 inf,不会改变线段树上的定位结果。二项式矩和中间乘积使用 __int128 保存。
建立全部版本需要
正确性证明
长度公式的初值为
第
版本差精确筛出 count_full 不重不漏地统计全部完整块。
最后,对递归深度归纳。当前层先正确计入所有完整块,再只递归到唯一的末尾块;后续块不属于前缀。深度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using i128=__int128;
const int N=100005;
const int M=N*19;
const ll inf=0x3f3f3f3f3f3f3f3f;
struct node
{
int l,r;
ll sum[5];
i128 c[4];
}tr[M];
int n,tot;
int rt[N],a[N],pos[N];
i128 C(ll n,int m)
{
i128 res=1;
for(int i=1;i<=m;i++)res=res*(n-i+1)/i;
return res;
}
int update(int pre,int l,int r,int x,int val)
{
int u=++tot;
tr[u]=tr[pre];
if(l==r)
{
for(int i=0;i<=4;i++)tr[u].sum[i]=min((i128)inf,C(val+i-1,i));
for(int i=0;i<=3;i++)tr[u].c[i]=C(val,i);
return u;
}
int mid=(l+r)>>1;
if(x<=mid)tr[u].l=update(tr[pre].l,l,mid,x,val);
else tr[u].r=update(tr[pre].r,mid+1,r,x,val);
for(int i=0;i<=4;i++)tr[u].sum[i]=min(inf,tr[tr[u].l].sum[i]+tr[tr[u].r].sum[i]);
for(int i=0;i<=3;i++)tr[u].c[i]=tr[tr[u].l].c[i]+tr[tr[u].r].c[i];
return u;
}
int kth(int u,int l,int r,int d,ll &k)
{
if(l==r)return l;
int mid=(l+r)>>1;
if(k<=tr[tr[u].l].sum[d])return kth(tr[u].l,l,mid,d,k);
k-=tr[tr[u].l].sum[d];
return kth(tr[u].r,mid+1,r,d,k);
}
void query(int u,int v,int l,int r,int x,i128 res[4])
{
if(x<l)return;
if(r<=x)
{
for(int i=0;i<=3;i++)res[i]+=tr[u].c[i]-tr[v].c[i];
return;
}
int mid=(l+r)>>1;
query(tr[u].l,tr[v].l,l,mid,x,res);
if(x>mid)query(tr[u].r,tr[v].r,mid+1,r,x,res);
}
ll count_full(int d,int x,int k,int p)
{
if(d==0)return pos[k]<p;
i128 v[4]={};
query(rt[x],rt[k-1],1,n,p-1,v);
int r=d-1;
i128 res=0;
for(int i=0;i<=r;i++)res+=C(r-k,r-i)*v[i];
return res;
}
ll solve(int d,int x,int k,ll len)
{
if(k>x)return 0;
if(d==0)return x==k;
int p=kth(rt[x],1,n,d-1,len);
return count_full(d-1,x,k,p)+solve(d-1,a[p],k,len);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int s,q;
cin>>n>>s>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
pos[a[i]]=i;
}
for(int i=1;i<=n;i++)rt[i]=update(rt[i-1],1,n,pos[i],i);
while(q--)
{
int k;
ll len;
cin>>k>>len;
cout<<solve(s,n,k,len)<<'\n';
}
return 0;
}