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 - 思考人生中 的博客 - 洛谷博客

Codeforces Round 803 (Div. 2) E. PermutationForces II(思维题 位置序列)_第1张图片

题解

首先考虑无解的情况,由于第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
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 P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<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;
}

你可能感兴趣的:(思维题,思维题,位置序列)