20251213NOI模拟赛
20251213NOI模拟赛
T1.挑战NPC
题面:
有 \(n\) 个逻辑变量 \(x_1,\ldots,x_n\),其中每一个变量 \(x_i\in \{{\rm true,false}\}\)。我们定义一个如下形式的表达式为合法表达式:
每一个 \(l_{i,j}\) 都是形如 \(x_k\) 或 \(\neg x_k\) 的形式,表示 \(x_k\) 这个逻辑变量或 \(x_k\) 取反。
现在小 E 在研究一种特殊的合法表达式,这个表达式有如下性质:
1.不存在 \(i,j_1,j_2,k\),使得 \(l_{i,j_1}\) 和 \(l_{i,j_2}\) 都是关于 \(x_k\) 的变量。
2.对于一个 \(i\),若存在 \(l_{i,j_1}\) 是关于 \(x_p\) 的变量,\(l_{i,j_2}\) 是关于 \(x_q\) 的变量,且 \(p<q\),则对于任意 \([p,q]\) 区间内的整数 \(k\),都存在 \(1\leq j\leq m_i\) 使得 \(l_{i,j}\) 是关于\(x_k\) 的变量。
小 E 不仅希望你求出解的数量。请你输出满足给定表达式为 true 的 \(x_1,x_2,\ldots,x_n\) 的解的数量。由于答案可能很大,你只需要输出答案对 \(10^9+7\) 取模的结果。\(n\leq 10^6\)
题解:
顺次决定 \(x_i\) 的值,每次会使得某些限制成立,需要维护的是到目前位置还没有成立的限制有哪些。记录这些没成立的限制中左端点最小的限制编号 \(k\),由此 \([l_k,i]\) 中每个 \(x_j\) 的取值是唯一确定的,所以一个限制 \(p\) 没有成立当且仅当它与 \(k\) 在区间 \([l_p,i]\) 的值都相同。
有一个 \(n^2\) 做法即记录 \(f_{i,k}\),找到所有 \(p\) 可以使用哈希。
65 分的代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=3005,B=31,D=233,MOD=19491001,P=19260817,mod=1e9+7;
int L[N],R[N],n,m;
vector<int>a[N],vec[N];
struct node{
ll x,y;
friend bool operator == (const node x,const node y){return x.x==y.x&&x.y==y.y;}
friend bool operator != (const node x,const node y){return x.x!=y.x||x.y!=y.y;}
friend node operator + (const node x,const node y){
node z={x.x+y.x,x.y+y.y};
if(z.x>=MOD) z.x-=MOD; if(z.y>=P) z.y-=P;
return z;
}
friend node operator - (const node x,const node y){
node z={x.x-y.x,x.y-y.y};
if(z.x<0) z.x+=MOD; if(z.y<0) z.y+=P;
return z;
}
friend node operator * (const node x,const node y){return {x.x*y.x%MOD,x.y*y.y%P};}
friend node operator * (const node x,int b){return {x.x*b%MOD,x.y*b%P};}
node add(int b){ return {(x*B+b)%MOD,(y*D+b)%P}; }
}fac[N];
vector<node>has[N];
int f[N][N];
void Add(int &x,int y){
x+=y;
if(x>=mod) x-=mod;
}
void READ(){
while(getchar()!='(');
while(1){
vector<pair<int,int> >tmp;
while(1){
char c=getchar();
if(c==')') break;
if(c=='~'){
c=getchar();
int s=0;
c=getchar();
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
tmp.push_back({s,-1});
if(c==')') break;
}
else if(c=='x'){
int s=0;
c=getchar();
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
tmp.push_back({s,1});
if(c==')') break;
}
}
sort(tmp.begin(),tmp.end());
++m;
L[m]=tmp.front().first; R[m]=tmp.back().first;
for(pair<int,int> x:tmp) a[m].push_back(x.second+2);
has[m].push_back({0,0});
for(int x:a[m]){
node p=has[m].back();
has[m].push_back(p.add(x));
}
for(int i=L[m];i<=R[m];i++) vec[i].push_back(m);
while(1){
char c=getchar();
if(c=='\n') return ;
if(c=='(') break;
}
}
}
node query(int i,int l,int r){
return has[i][r]-has[i][l-1]*fac[r-l+1];
}
bool check(int x,int y,int i){
if(L[y]<L[x]) return 0;
return query(x,L[y]-L[x]+1,i-L[x]+1)==query(y,1,i-L[y]+1);
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();
fac[0]={1,1}; for(int i=1;i<=n;i++) fac[i]=fac[i-1].add(0);
READ();
// cout<<m<<endl;
// for(int i=1;i<=m;i++){
// cout<<L[i]<<' '<<R[i]<<endl;
// for(int x:a[i]) cout<<x<<' ';cout<<endl;
// for(node x:has[i]) cout<<x.x<<' '<<x.y<<' ';cout<<endl;
// }
f[0][0]=1;
for(int i=0;i<n;i++){
//get 0
{
//pre 0
{
int nv=0;
bool fl=0;
for(int x:vec[i+1])
if(L[x]>i){
if(R[x]==i+1&&a[x].back()!=1){
fl=1;
break;
}
}
if(!fl){
for(int x:vec[i+1])
if(L[x]>i&&a[x][0]==3){
nv=x;
break;
}
Add(f[i+1][nv],f[i][0]);
}
}
for(int j=1;j<=m;j++) if(f[i][j]){
bool fl=0;
int nv=0;
for(int x:vec[i+1])
if(check(j,x,i)){
if(R[x]==i+1&&a[x].back()!=1){
fl=1;
break;
}
if(a[x][i+1-L[x]]!=1){
if(!nv||L[x]<L[nv]) nv=x;
}
}
if(!fl) Add(f[i+1][nv],f[i][j]);
}
}
//1
{
//0
{
int nv=0;
bool fl=0;
for(int x:vec[i+1])
if(L[x]>i){
if(R[x]==i+1&&a[x].back()!=3){
fl=1;
break;
}
}
if(!fl){
for(int x:vec[i+1])
if(L[x]>i&&a[x][0]==1){
nv=x;
break;
}
Add(f[i+1][nv],f[i][0]);
}
}
for(int j=1;j<=m;j++) if(f[i][j]){
bool fl=0;
int nv=0;
for(int x:vec[i+1])
if(check(j,x,i)){
if(R[x]==i+1&&a[x].back()!=3){
fl=1;
break;
}
if(a[x][i+1-L[x]]!=3){
if(!nv||L[x]<L[nv]) nv=x;
}
}
if(!fl) Add(f[i+1][nv],f[i][j]);
}
}
}
printf("%d",f[n][0]);
return 0;
}
上述 DP 总状态数是 \(\sum m_i\) 的,复杂度在于转移。
考虑在转移 \(i\) 时,将所有串从 \(i\to l_j\) 插入 trie 树,如果 \(k\) 在 \(i\) 位置仍不合法则状态还是 \(k\),否则转移到 \(k\) 在 trie 的祖先中最深的不合法的点。对于所有的 \(k\) 求出这样的点每次复杂度是 trie 大小,总复杂度 \(\sum m_i\)。
现在问题在于如何维护 trie。需要支持在每个串前加入一个字符,并删除一些串。做法是将不删除的串按下一位是 \(0/1\) 分类建两颗虚树,然后新建新根连向两个虚树的根,因为我们不需要关心下面 trie 的边是 \(0/1\) 而只关心他们之间的祖先关系。上述操作复杂度都是 trie 大小,总复杂度 \(\sum m_i\)。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=1e6+5,mod=1e9+7,M=N*3;
int L[N],R[N],n,m,pos[N],tr[M];
vector<int>a[N],vec[N],e[M],G[M],nod,ids[M],tmp[M];
int f[N],g[N],nxt[M][2];
int tot,rt,rtx,rty;
bool ok[M][2],vis[M][2];
void Add(int &x,int y){
x+=y;
if(x>=mod) x-=mod;
}
void READ(){
while(getchar()!='(');
while(1){
vector<pair<int,int> >tmp;
while(1){
char c=getchar();
if(c==')') break;
if(c=='~'){
c=getchar();
int s=0;
c=getchar();
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
tmp.push_back({s,-1});
if(c==')') break;
}
else if(c=='x'){
int s=0;
c=getchar();
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
tmp.push_back({s,1});
if(c==')') break;
}
}
sort(tmp.begin(),tmp.end());
++m;
L[m]=tmp.front().first; R[m]=tmp.back().first;
for(pair<int,int> x:tmp) a[m].push_back(x.second==1?1:0);
for(int i=L[m];i<=R[m];i++) vec[i].push_back(m);
while(1){
char c=getchar();
if(c=='\n') return ;
if(c=='(') break;
}
}
}
void cmin(int &x,int y){
if(!x||(y&&L[x]>L[y])) x=y;
}
void dfs(int x){
for(int v:e[x]){
ok[v][0]|=ok[x][0];
ok[v][1]|=ok[x][1];
cmin(nxt[v][0],nxt[x][0]);
cmin(nxt[v][1],nxt[x][1]);
dfs(v);
}
}
void dfz(int x){
for(int v:e[x]){
dfz(v);
ok[x][0]|=ok[v][0];
ok[x][1]|=ok[v][1];
}
}
bool check(int x,int v){
return vis[x][v]||(e[x].size()==2&&ok[e[x][0]][v]&&ok[e[x][1]][v]);
}
void dfp(int x,int rtx,int rty,int T){
if(check(x,0)&&check(x,1)){
vector<int>vec[2];
for(int v:ids[x]){
if(R[v]<=T) continue;
vec[a[v][T-L[v]]].push_back(v);
}
if(rtx) G[rtx].push_back(x);
else G[rt].push_back(x);
rtx=x;
ids[rtx]=vec[0];
for(int v:vec[0]) pos[v]=rtx;
++tot;
if(rty) G[rty].push_back(tot);
else G[rt].push_back(tot);
rty=tot;
ids[rty]=vec[1];
for(int v:vec[1]) pos[v]=rty;
nod.push_back(rtx);
nod.push_back(rty);
}
else if(check(x,0)){
if(rtx) G[rtx].push_back(x);
else G[rt].push_back(x);
rtx=x;
nod.push_back(x);
}
else if(check(x,1)){
if(rty) G[rty].push_back(x);
else G[rt].push_back(x);
rty=x;
nod.push_back(x);
}
for(int v:e[x]) dfp(v,rtx,rty,T);
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();
READ();
tot=rt=1;
f[0]=1;
for(int T=0;T<n;T++){
for(int x:vec[T+1]) g[x]=0;g[0]=0;
bool fl0=0,fl1=0;
{
int nv0=0,nv1=0;
for(int x:vec[T+1])
if(L[x]>T){
if(R[x]==T+1){
if(a[x].back()==0) fl1=1;
else fl0=1;
}
if(a[x][0]==1) nv0=x;
else nv1=x;
}
if(!fl0) Add(g[nv0],f[0]);
if(!fl1) Add(g[nv1],f[0]);
}
for(int x=1;x<=tot;x++) ok[x][0]=ok[x][1]=0,nxt[x][0]=nxt[x][1]=0;
for(int x:vec[T+1]){
if(R[x]==T+1) ok[pos[x]][a[x].back()^1]=1;
else cmin(nxt[pos[x]==0?rt:pos[x]][a[x][T+1-L[x]]^1],x);
}
dfs(rt);
for(int x:vec[T]){
if(R[x]==T) continue;
if(!fl0&&!ok[pos[x]][0]) Add(g[nxt[pos[x]][0]],f[x]);
if(!fl1&&!ok[pos[x]][1]) Add(g[nxt[pos[x]][1]],f[x]);
}
for(int x:vec[T+1]) f[x]=g[x];f[0]=g[0];
for(int x=1;x<=tot;x++) ok[x][0]=ok[x][1]=vis[x][0]=vis[x][1]=0;
rtx=0;rty=0;
for(int x:vec[T+1]){
if(R[x]==T+1) continue;
if(L[x]==T+1){
int v=a[x][0];
if(v==0){
if(!rtx) rtx=++tot;
pos[x]=rtx;
ids[rtx].push_back(x);
}
else{
if(!rty) rty=++tot;
pos[x]=rty;
ids[rty].push_back(x);
}
}
else{
int v=a[x][T+1-L[x]];
ok[pos[x]][v]=vis[pos[x]][v]=1;
}
}
dfz(rt);
nod.clear();nod.push_back(rt);
if(check(rt,0)&&!rtx) rtx=++tot;
if(check(rt,1)&&!rty) rty=++tot;
for(int v:e[rt]) dfp(v,rtx,rty,T+1);
if(rtx) G[rt].push_back(rtx);
if(rty) G[rt].push_back(rty);
nod.push_back(rtx);nod.push_back(rty);
for(int i=1;i<=tot;i++) e[i].clear();
tot=0;
for(int x:nod) tr[x]=++tot;
for(int x:vec[T+1]) pos[x]=tr[pos[x]];
for(int x:nod) for(int v:G[x]) e[tr[x]].push_back(tr[v]);
for(int x:nod) G[x].clear();
for(int x:nod) swap(tmp[tr[x]],ids[x]);
for(int i=1;i<=tot;i++) swap(ids[i],tmp[i]);
}
printf("%d",f[0]);
return 0;
}
T2.删排列 delete
题面:
有一个长 \(n\) 的排列 \(p\),每次选择一个数删除,代价是 \(|i-p_i|\)。删除后 \(i\) 后位置全部前移一位,大于 \(p_i\) 的值全部减一,变成一个 \(n-1\) 排列。求所有方案中最大操作代价最小的值。\(n\leq 5\times 10^5\)
题解:
假设操作最大值是 \(m\),发现若开始时 \(|i-p_i|\) 小于等于 \(m\) 或大于 \(m\),则整个操作过程中这个关系一定保持不变,证明考虑分讨另一个操作 \((j,p_j)\) 与 \((i,p_i)\) 各个变量的大小关系。
所以有 \(n^2\) 做法是每次取最小的 \(|i-p_i|\) 操作,并在此之后暴力更新剩余操作。
进一步的发现 \(i\) 与 \(p_i\) 的大小关系也不会改变,所以分别维护 \(i<p_i\) 和 \(i>p_i\) 的点对,每次取出两者间最小值进行操作。以 \(i<p_i\) 为例,\(i>p_i\) 同理。发现对于一个点对 \((i,p_i)\) 若 \((j,p_j)\) 满足 \(j>i,p_j<p_i\) 则 \(j\) 删除前不用考虑 \(i\),所以考虑线段树维护出所有这些关键点。
一次删除操作会使关键点的一个区间代价减一。删除一个关键点后会有新的关键点显露出来,每次线段树二分找到一个新关键点,复杂度总和是 \(n\log n\)。两个树状数组维护已删的 \(i,p_i\) 集合,以便找出新关键点后快速算出其当前权值,并加入关键点线段树中。对于 \(i<p_i\) 和 \(i>p_i\) 分别维护上述信息。复杂度 \(n\log n\)。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=5e5+5,inf=1e9+7;
int n,a[N];
set<int>up,dn;
struct BIT{
int c[N];
void clear(int n){
for(int i=0;i<=n;i++) c[i]=0;
}
void add(int x,int v){
while(x<=n){
c[x]+=v;
x+=x&-x;
}
}
int query(int x){
int res=0;
while(x>0){
res+=c[x];
x-=x&-x;
}
return res;
}
}X,Y;
namespace dnp{
struct Tree{
int v,s,tag,x;
}t[N<<2];
void pushup(int s){
t[s].v=max(t[s<<1].v,t[s<<1|1].v);
t[s].s=min(t[s<<1].s,t[s<<1|1].s);
if(t[s].s==t[s<<1].s) t[s].x=t[s<<1].x;
else t[s].x=t[s<<1|1].x;
}
void upd(int s,int v){
t[s].s+=v;
t[s].tag+=v;
}
void pushdown(int s){
if(t[s].tag){
upd(s<<1,t[s].tag);
upd(s<<1|1,t[s].tag);
t[s].tag=0;
}
}
void build(int s,int l,int r){
t[s]={-inf,inf,0,l};
if(l==r) return ;
pushdown(s);
int mid=l+r>>1;
build(s<<1,l,mid);
build(s<<1|1,mid+1,r);
}
void update(int s,int l,int r,int L,int R,int v){
if(L<=l&&r<=R){
upd(s,v);
return ;
}
pushdown(s);
int mid=l+r>>1;
if(L<=mid) update(s<<1,l,mid,L,R,v);
if(R>mid) update(s<<1|1,mid+1,r,L,R,v);
pushup(s);
}
void modify(int s,int l,int r,int x,int S,int v){
if(l==r){
t[s].s=S;t[s].v=v;
return ;
}
pushdown(s);
int mid=l+r>>1;
if(x<=mid) modify(s<<1,l,mid,x,S,v);
else modify(s<<1|1,mid+1,r,x,S,v);
pushup(s);
}
int erfen(int s,int l,int r,int x,int v){
if(t[s].v<=v) return n+1;
if(l==r) return l;
pushdown(s);
int mid=l+r>>1,res=n+1;
if(x<=mid) res=erfen(s<<1,l,mid,x,v);
if(res==n+1) res=erfen(s<<1|1,mid+1,r,x,v);
return res;
}
}
namespace ups{
int t[N<<2];
void pushup(int s){
t[s]=min(t[s<<1],t[s<<1|1]);
}
void build(int s,int l,int r){
t[s]=inf;
if(l==r) return ;
int mid=l+r>>1;
build(s<<1,l,mid);
build(s<<1|1,mid+1,r);
}
void modify(int s,int l,int r,int x,int v){
if(l==r){
t[s]=v;
return ;
}
int mid=l+r>>1;
if(x<=mid) modify(s<<1,l,mid,x,v);
else modify(s<<1|1,mid+1,r,x,v);
pushup(s);
}
int erfen(int s,int l,int r,int x,int v){
if(t[s]>=v) return 0;
if(l==r) return l;
int mid=l+r>>1,res=0;
if(x>mid) res=erfen(s<<1|1,mid+1,r,x,v);
if(!res) res=erfen(s<<1,l,mid,x,v);
return res;
}
}
namespace upp{
struct Tree{
int v,s,tag,x;
}t[N<<2];
void pushup(int s){
t[s].v=min(t[s<<1].v,t[s<<1|1].v);
t[s].s=min(t[s<<1].s,t[s<<1|1].s);
if(t[s].s==t[s<<1].s) t[s].x=t[s<<1].x;
else t[s].x=t[s<<1|1].x;
}
void upd(int s,int v){
t[s].s+=v;
t[s].tag+=v;
}
void pushdown(int s){
if(t[s].tag){
upd(s<<1,t[s].tag);
upd(s<<1|1,t[s].tag);
t[s].tag=0;
}
}
void build(int s,int l,int r){
t[s]={inf,inf,0,l};
if(l==r) return ;
int mid=l+r>>1;
build(s<<1,l,mid);
build(s<<1|1,mid+1,r);
}
void update(int s,int l,int r,int L,int R,int v){
if(L<=l&&r<=R){
upd(s,v);
return ;
}
pushdown(s);
int mid=l+r>>1;
if(L<=mid) update(s<<1,l,mid,L,R,v);
if(R>mid) update(s<<1|1,mid+1,r,L,R,v);
pushup(s);
}
void modify(int s,int l,int r,int x,int S,int v){
if(l==r){
t[s].s=S;t[s].v=v;
return ;
}
pushdown(s);
int mid=l+r>>1;
if(x<=mid) modify(s<<1,l,mid,x,S,v);
else modify(s<<1|1,mid+1,r,x,S,v);
pushup(s);
}
int erfen(int s,int l,int r,int x,int v){
if(t[s].v>=v) return 0;
if(l==r) return l;
int mid=l+r>>1,res=0;
pushdown(s);
if(x>mid) res=erfen(s<<1|1,mid+1,r,x,v);
if(!res) res=erfen(s<<1,l,mid,x,v);
return res;
}
}
namespace dns{
int t[N<<2];
void pushup(int s){
t[s]=max(t[s<<1],t[s<<1|1]);
}
void build(int s,int l,int r){
t[s]=-inf;
if(l==r) return ;
int mid=l+r>>1;
build(s<<1,l,mid);
build(s<<1|1,mid+1,r);
}
void modify(int s,int l,int r,int x,int v){
if(l==r){
t[s]=v;
return ;
}
int mid=l+r>>1;
if(x<=mid) modify(s<<1,l,mid,x,v);
else modify(s<<1|1,mid+1,r,x,v);
pushup(s);
}
int erfen(int s,int l,int r,int x,int v){
if(t[s]<=v) return n+1;
if(l==r) return l;
int mid=l+r>>1,res=n+1;
if(x<=mid) res=erfen(s<<1,l,mid,x,v);
if(res==n+1) res=erfen(s<<1|1,mid+1,r,x,v);
return res;
}
}
void solve(){
n=read();
X.clear(n);Y.clear(n);
upp::build(1,1,n);ups::build(1,1,n);
dnp::build(1,1,n);dns::build(1,1,n);
up.clear();dn.clear();
a[0]=-inf;for(int i=1;i<=n;i++) a[i]=read();a[n+1]=inf;
{
int lst=inf;
for(int i=n;i>=1;i--)
if(i<a[i]){
if(a[i]<lst){
upp::modify(1,1,n,i,a[i]-i,a[i]);
up.insert(i);
lst=a[i];
}
else ups::modify(1,1,n,i,a[i]);
}
}
{
int lst=-inf;
for(int i=1;i<=n;i++)
if(a[i]<=i){
if(a[i]>lst){
dnp::modify(1,1,n,i,i-a[i],a[i]);
dn.insert(i);
lst=a[i];
}
else dns::modify(1,1,n,i,a[i]);
}
}
int ans=0;
for(int T=1;T<=n;T++){
if(upp::t[1].s<dnp::t[1].s){
ans=max(ans,upp::t[1].s);
int x=upp::t[1].x;
upp::modify(1,1,n,x,inf,inf);
X.add(x,1); Y.add(a[x],1);
int p;
if(x+1<=n) p=dnp::erfen(1,1,n,x+1,a[x]);
else p=n+1;
if(x+1<=p-1) dnp::update(1,1,n,x+1,p-1,-1);
int v,lim;
auto it=up.find(x);
if(next(it)==up.end()) v=inf;
else v=a[*next(it)];
if(it==up.begin()) lim=0;
else lim=*prev(it);
up.erase(it);
while(lim<x-1){
x=ups::erfen(1,1,n,x-1,v);
if(x<=lim) break;
v=a[x];
upp::modify(1,1,n,x,(v-Y.query(v))-(x-X.query(x)),v);
ups::modify(1,1,n,x,inf);
up.insert(x);
}
}
else{
ans=max(ans,dnp::t[1].s);
int x=dnp::t[1].x;
dnp::modify(1,1,n,x,inf,-inf);
X.add(x,1); Y.add(a[x],1);
int p;
if(1<=x-1) p=upp::erfen(1,1,n,x-1,a[x]);
else p=0;
if(p+1<=x-1) upp::update(1,1,n,p+1,x-1,-1);
int v,lim;
auto it=dn.find(x);
if(it==dn.begin()) v=-inf;
else v=a[*prev(it)];
if(next(it)==dn.end()) lim=n+1;
else lim=*next(it);
dn.erase(it);
while(x+1<lim){
x=dns::erfen(1,1,n,x+1,v);
if(x>=lim) break;
v=a[x];
dnp::modify(1,1,n,x,(x-X.query(x))-(v-Y.query(v)),v);
dns::modify(1,1,n,x,-inf);
dn.insert(x);
}
}
}
printf("%d\n",ans);
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int T=read();
while(T--) solve();
return 0;
}
T3.查商品 check
题面:
给一个长为 \(n\) 的环形排列 \(a\),最初你在 \(1\) 位置。每次你决策有 \(p\) 的概率删除 \(a_i\),然后不管怎样都移动到下一个没有删除的位置。求最后全部删除后删除序列在所有 \(n\) 阶排列中字典序排名的期望值。\(n\leq 5000\)
题解:
考虑确定一个排列怎么求其字典序排名,设排列为 \(a_i\),其排名为 \(1+\sum_i (n-i)!\sum_{j>i} [a_j<a_i]\)。
考虑环上 \(i\) 位置的元素删除时间是 \(t_i\),则序列排名为 \(1+\sum_i (n-t_i)!\sum_{t_i<t_j}[a_i>a_j]\)。
所以答案期望为 \(1+\sum_i\sum_j\sum_{t_i}[a_i>a_j]P(t_i<t_j)(n-t_i)!\)。
令 \(q=1-p\)。
设 \(f_{n,i,j}\) 表示长度为 \(n\) 的环,\(i\) 先于 \(j\) 消失的情况下 \((n-t_i)!\) 的期望。
转移时考虑对物品重标号,将当前需要决策的物品标号为 1。考虑下一个物品时将下一个重标号为 1,则 \(i,j\) 的标号转移到 \(i-1,j-1\)。如果删除当前物品则 \(n\to n-1\)。考虑一些边界情况总体转移是:
注意此处因为每删除一个物品则 \(n\to n-1\) 所以当删除 \(i\) 时 \(n-t_i\) 就是此时的 \(n-1\)。
进一步发现,当决策 \(i\) 时,\(1\sim i-1\) 的物品消失概率是相同的,\(i+1\sim n\) 的物品消失概率是相同的。即 \(f_{n,i,j}\) 的值对于 \([1,i-1]\) 和 \([i+1,n]\) 分别全相等,或者你可以通过打表 \(f_{n,i,*}\) 发现这一点。
所以我们只需求 \(f_{n,i}\) 表示是上述 \(f_{n,i,i-1}\),\(g_{n,i}\) 表示上述 \(f_{n,i,i+1}\) 即可。
转移照搬上述方程式:
不要高斯消元,注意到对于每个 \(n\),转移方程形成了一个环,可以 \(O(n)\) 计算,总复杂度 \(O(n^2)\)。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=5005,mod=1e9+7;
int n,a[N];
ll p,q,fac[N],f[N],g[N],B[N];
ll Mod(ll x){return x>=mod?x-mod:x;}
void Add(ll &x,ll y){x=Mod(x+y);}
ll ksm(ll a,ll b){
ll t=1;
for(;b;b>>=1,a=a*a%mod)
if(b&1) t=t*a%mod;
return t;
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();p=read();q=Mod(1+mod-p);
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
for(int i=1;i<=n;i++) a[i]=read();
for(int T=2;T<=n;T++){
{
B[1]=p*fac[T-1]%mod; B[2]=0;
for(int i=3;i<=T;i++) B[i]=f[i-1]*p%mod;
ll k=1,b=0;
for(int i=1;i<=T;i++){
(k*=q)%=mod;
b=(b*q+B[i])%mod;
}
f[0]=b*ksm(Mod(1+mod-k),mod-2)%mod;
for(int i=1;i<=T;i++) f[i]=(f[i-1]*q+B[i])%mod;
}
{
B[1]=p*fac[T-1]%mod; B[T]=0;
for(int i=2;i<T;i++) B[i]=g[i-1]*p%mod;
ll k=1,b=0;
for(int i=1;i<=T;i++){
(k*=q)%=mod;
b=(b*q+B[i])%mod;
}
g[0]=b*ksm(Mod(1+mod-k),mod-2)%mod;
for(int i=1;i<=T;i++) g[i]=(g[i-1]*q+B[i])%mod;
}
}
ll ans=1;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++) if(a[i]>a[j]) Add(ans,f[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++) if(a[i]>a[j]) Add(ans,g[i]);
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号