洛谷 NOIP 2023 模拟赛 挑战 NPC IV

洛谷 NOIP 2023 模拟赛 挑战 NPC IV

题目大意

f ( x ) = 1 + log ⁡ 2 lowbit ( x ) f(x)=1+\log_2\text{lowbit}(x) f(x)=1+log2lowbit(x),对于一个 1 1 1 n n n的排列 p i p_i pi,其权值为 ∑ l = 1 n ∑ r = l n ∑ i = l r f ( p i ) \sum\limits_{l=1}^n\sum\limits_{r=l}^n\sum\limits_{i=l}^rf(p_i) l=1nr=lni=lrf(pi)。求一个 1 1 1 n n n的排列 p i p_i pi,使得这个排列是所有排列中第 k k k小的。输出第 k k k小的权值模 998244353 998244353 998244353后的值。

q q q组数据。

测试点编号 n n n k ≤ k \leq k q = q= q=
1 ∼ 3 1 \sim 3 13 ≤ 10 \leq 10 10 n ! n! n! 2 2 2
4 ∼ 8 4 \sim 8 48 ≤ 1 0 3 \leq 10^3 103 2 2 2 7 7 7
9 ∼ 13 9 \sim 13 913 ∈ [ 1 0 5 , 1 0 6 ] \in [10^5, 10^6] [105,106] min ⁡ ( 1 0 18 , n ! ) \min(10^{18}, n!) min(1018,n!) 7 7 7
14 ∼ 17 14 \sim 17 1417 ≤ 1 0 6 \leq 10^6 106 min ⁡ ( 1 0 18 , n ! ) \min(10^{18}, n!) min(1018,n!) 7 7 7
18 ∼ 25 18 \sim 25 1825 ≤ 1 0 18 \leq 10^{18} 1018 min ⁡ ( 1 0 18 , n ! ) \min(10^{18}, n!) min(1018,n!) 10 10 10

对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 18 1 \leq n \leq 10^{18} 1n1018 1 ≤ k ≤ min ⁡ ( 1 0 18 , n ! ) 1 \leq k \leq \min(10^{18}, n!) 1kmin(1018,n!) 1 ≤ q ≤ 10 1 \leq q\le 10 1q10

时间限制为 max ⁡ ( q × 0.5 , 2 ) s \max(q\times 0.5, 2)s max(q×0.5,2)s,空间限制为 512 M B 512MB 512MB


题解

首先,我们可以得出, ∑ l = 1 n ∑ r = l n ∑ i = l r f ( p i ) = ∑ i = 1 n f ( p i ) × i × ( n − i + 1 ) \sum\limits_{l=1}^n\sum\limits_{r=l}^n\sum\limits_{i=l}^rf(p_i)=\sum\limits_{i=1}^nf(p_i)\times i\times (n-i+1) l=1nr=lni=lrf(pi)=i=1nf(pi)×i×(ni+1)。记 c i = i × ( n − i + 1 ) c_i=i\times (n-i+1) ci=i×(ni+1) d i = f ( p i ) d_i=f(p_i) di=f(pi)

我们先考虑 k = 1 k=1 k=1的情况,也就是要最小化 c i × d i c_i\times d_i ci×di的和,我们用最大的 c c c值来匹配最小的 d d d值,次大的 c c c值来匹配次小的 d d d值,以此类推。

k = 2 k=2 k=2时,我们在权值最小的排列 p i p_i pi的基础上,交换 p 1 p_1 p1 p n p_n pn的位置,权值不变,也就是说 k = 2 k=2 k=2时的答案与 k = 1 k=1 k=1的时的答案相同。

k = 2 k=2 k=2的情况的启发下,我们发现:

  • p i p_i pi p j p_j pj的最低位 1 1 1的位置相同,则交换 p i p_i pi p j p_j pj不会改变权值
  • 交换 p i p_i pi p n − i + 1 p_{n-i+1} pni+1也不会改变权值

我们推测:权值取到最小值的排列有很多。

事实上,当 n = 29 n=29 n=29时,这样的排列数量就已经超过了 1 0 18 10^{18} 1018,所以,当 n ≥ 29 n\geq 29 n29时,答案与 k k k无关,始终为最小的权值。

如果按上面那样做是 O ( n ) O(n) O(n)的,下面考虑优化。

对于每一种 f f f值,我们需要查询 c c c值从小到大排序后一段区间的和,推式子即可,时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)的。

n ≤ 28 n\leq 28 n28时,我们可以用 D P DP DP来做。

f [ a ] [ b ] [ c ] [ d ] [ e ] [ s ] f[a][b][c][d][e][s] f[a][b][c][d][e][s]表示目前已经填了前 i = a + b + c + d + e i=a+b+c+d+e i=a+b+c+d+e个数,其中有 a a a个数的最低位为 1 1 1 b b b个数的最低位为 2 2 2,以此类推,且当前的权值为 s s s

我们枚举下一个位置的最低位 1 1 1的位置 t t t,那么新状态的权值为 s + c i + 1 × t s+c_{i+1}\times t s+ci+1×t

我们发现,从最低位 1 1 1的位置为 1 1 1 5 5 5的数的数量分别为 14 , 7 , 4 , 2 , 1 14,7,4,2,1 14,7,4,2,1,还要算上 0 0 0的情况,最大的权值不会超过 10000 10000 10000,那么总状态数 S S S不超过 15 × 8 × 5 × 3 × 2 × 10000 = 3.6 × 1 0 7 15\times 8\times 5\times 3\times 2\times 10000=3.6\times 10^7 15×8×5×3×2×10000=3.6×107,那么 D P DP DP的时间复杂度为 O ( 5 S ) O(5S) O(5S)

总时间复杂度为 O ( q ( log ⁡ n + 5 S ) ) O(q(\log n+5S)) O(q(logn+5S))

code

#include
using namespace std;
const int N=30,V=10000;
const long long mod=998244353;
const long long ny2=499122177,ny6=166374059;
int T,c[N+5],w[10];
long long n,k,ans,lst,now,f[15][8][5][3][2][V+5];
long long gt2(long long t){
	t%=mod;
	return t*(t+1)%mod*ny2%mod;
}
long long gt6(long long t){
	t%=mod;
	return t*(t+1)%mod*(2*t+1)%mod*ny6%mod;
}
long long gt(long long t){
	long long re=(t&1)*(t/2+1)%mod*((n-(t/2+1)+1)%mod)%mod;t/=2;
	long long tmp=((n+1)%mod*gt2(t)%mod-gt6(t)+mod)%mod;
	re=(re+tmp*2)%mod;
	return re;
}
void pt(long long *a,long long *b,int t,int p){
	for(int i=0;i<=V;i++){
		if(b[i]) a[i+t]+=b[i]*p;
	}
}
void gt(long long *a){
	for(int i=0;i<=V;i++){
		if(k>a[i]) k-=a[i];
		else{
			printf("%d\n",i);
			return;
		}
	}
}
int main()
{
	scanf("%d",&T);
	while(T--){
		scanf("%lld%lld",&n,&k);
		ans=0;
		if(n>=29){
			lst=now=0;
			for(int i=60;i>=1;i--){
				now=lst+(n+(1ll<<i-1))/(1ll<<i);
				ans=(ans+(gt(now)-gt(lst)+mod)%mod*i%mod)%mod;
				lst=now;
			}
			printf("%lld\n",ans);
			continue;
		}
		for(int i=1;i<=n;i++){
			c[i]=i*(n-i+1);
		}
		for(int i=1;i<=5;i++){
			w[i]=(n+(1<<i-1))/(1<<i);
		}
		memset(f,0,sizeof(f));
		f[0][0][0][0][0][0]=1;
		for(int v1=0;v1<=w[1];v1++)
		for(int v2=0;v2<=w[2];v2++)
		for(int v3=0;v3<=w[3];v3++)
		for(int v4=0;v4<=w[4];v4++)
		for(int v5=0;v5<=w[5];v5++){
			int sum=v1+v2+v3+v4+v5;
			if(v1<w[1]) pt(f[v1+1][v2][v3][v4][v5],f[v1][v2][v3][v4][v5],c[sum+1]*1,w[1]-v1);
			if(v2<w[2]) pt(f[v1][v2+1][v3][v4][v5],f[v1][v2][v3][v4][v5],c[sum+1]*2,w[2]-v2);
			if(v3<w[3]) pt(f[v1][v2][v3+1][v4][v5],f[v1][v2][v3][v4][v5],c[sum+1]*3,w[3]-v3);
			if(v4<w[4]) pt(f[v1][v2][v3][v4+1][v5],f[v1][v2][v3][v4][v5],c[sum+1]*4,w[4]-v4);
			if(v5<w[5]) pt(f[v1][v2][v3][v4][v5+1],f[v1][v2][v3][v4][v5],c[sum+1]*5,w[5]-v5);
		}
		gt(f[w[1]][w[2]][w[3]][w[4]][w[5]]);
	}
	return 0;
}

你可能感兴趣的:(题解,好题,题解,c++)