Codeforces Round 803 (Div. 2) E. PermutationForces II(思维题 位置序列)

题目

给定长为n(n<=2e5)的两个序列a和b,

a为n的一个排列,

b也为n的一个排列,但有一些位置被-1替换了,保证没被替换的位置在[1,n]之间且两两不同

你有一个距离最大限制s,你可以执行n次操作,

第i次操作时(1<=i<=n),

你可以选择满足i<=x<=y<=min(i+s,n)的两个值(x,y),

交换这两个值在a序列里的位置,

你可以选择x=y,也就是意味着可以不操作

现在你可以将b中的每个-1的位置替换上对应的[1,n]内的值,使得b是一个排列,

如果替换成排列之后的序列b,可以由a序列在n次交换后得到,则称b序列是合法的,

问所有合法的b序列的方案数,答案对998244353取模

实际为t(t<=1e3)组样例,保证sumn不超过2e5

思路来源

洛谷题解

CF1698E PermutationForces II Solution - 思考人生中 的博客 - 洛谷博客

题解

首先考虑无解的情况,由于第i次只能交换[i,i+s]内的两个值,

不妨第i次交换,就是使i这个值在b序列中归位

若b[i]需要被归位,且当前占住i位置的值a[i]>b[i]+s,

说明增序考虑到i时,a[i]是被换不走的,此时无解

所以,合法的条件是,对于b[i]不为-1的位置,要求a[i]的值不能超过b[i]+s

b[i]!=-1, max_{i=1}^{n}(a[i]-b[i]) \leq s

有解之后,考虑怎么操作,首先考虑给转换成位置序列

即,若a[i]=j,则令posa[j]=i;若b[i]=j,则令posb[j]=i

举一个例子,即第五个样例

原序列:

n=7,s=4

a: 1 3 6 2 7 4 5

b: 2 5 1 -1 -1 4 -1

转化序列:

posa: 1 4 2 6 7 3 5

posb: -1 1 -1 6 2 -1 -1

对于posb的每一个为-1的位置i,

只有posa序列[1,min(i+s,n)]中,那些值没有在posb中出现过的位置是可取的,

记这个值的个数为cnt,则对于每个posb的-1位置,

有cnt个值是可取的,每取走一个,即令ans*=cnt,cnt-=1

比如,在为posb[1]=-1这个未确定的位置赋值的时候,posb[1]的值,

可以在[1 4 2 6 7],也就是[1,1+s]这个区间里取,但是只能用[4 7]这两个值,

因为1 2 6在posb中出现过,只能换到一一对应的位置去

也就是说,4个-1位置可以用到的值是4 7 3 5,

其中,

posb[1]只能用到[1,5]里的可用位置,即[1 4 2 6 7]里的[4 7],ans*=2

posb[3]可以用到[1,7]里的可用位置,即[1 4 2 6 7 3 5]里的[4 7 3 5],

而其中[4 7]有一个被posb[1]用过,所以ans*=3

类似地,posb[6]和posb[7]都可以用到[1,7]里的可用位置,分别对应ans*=2和ans*=1

所以,ans=2*3*2*1=12

每个前面的不确定位置只能用一个值,前面没有用到的值可以被换下来换到后面, 

所以posb[i]=-1的位置,可以用到[1,min(i+s,n)]的所有可用位置

双指针模拟这个过程即可,复杂度O(n)

代码

#include<iostream>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=2e5+10,mod=998244353;
int t,n,s,a[N],b[N],posa[N],posb[N];
bool vis[N];
/*
原序列
n=7 s=4
a: 1 3 6 2 7 4 5
b: 2 5 1 -1 -1 4 -1
转化序列
posa: 1 4 2 6 7 3 5
posb: -1 1 -1 6 2 -1 -1
其中4 7 3 5是可用的-1位置,第一个位置只能用到[1 4 2 6 7]里的可用位置
对于posb的每一个为-1的位置i,只有posa中[1,min(i+s,n)]中,那些值没有在posb中出现过的位置是可取的,记这个值的个数为cnt,则对于每个posb的-1位置,ans*=cnt,cnt-=1
*/
int sol(){
    sci(n),sci(s);
    rep(i,1,n){
        sci(a[i]);
        posa[a[i]]=i;
        posb[i]=-1;
        vis[i]=0;
    }
    rep(i,1,n){
        sci(b[i]);
        if(b[i]==-1)continue;
        posb[b[i]]=i;
        vis[i]=1;
    }
    rep(i,1,n){
        if(b[i]==-1)continue;
        //printf("i:%d a:%d b:%d\n",i,a[i],b[i]);
        if(a[i]-b[i]>s)return 0; //若b[i]需要被归位,且当前占住i位置的值a[i]>b[i]+s,说明增序考虑到i时,a[i]是挪不走的,无解
    }
    int cur=0,ans=1,cnt=0;
    rep(i,1,n){
        while(cur+1<=min(n,i+s)){
            cur++;
            cnt+=(!vis[posa[cur]]);
        }
        //printf("i:%d cur:%d cnt:%d\n",i,cur,cnt);
        if(posb[i]==-1){
            ans=1ll*ans*cnt%mod;
            cnt--;
        }
    }
    return ans;
}
int main(){
    sci(t);
    while(t--){
        pte(sol());
    }
	return 0;
}

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值