首页 > 其他分享 >[题解] P9838 挑战 NPC IV

[题解] P9838 挑战 NPC IV

时间:2023-11-12 16:13:24浏览次数:35  
标签:return int 题解 pos IV P9838 权值 mul now

P9838 挑战 NPC IV

定义 \(f(x) = 1 + \log_2 \operatorname{lowbit}(x)\)。
定义一个 \(1 \sim n\) 的排列 \(p\) 的权值是 \(\sum_{l = 1}^n \sum_{r = l}^n \sum_{i \in [l, r]} f(p_i)。\)
求所有 \(1 \sim n\) 的排列中权值第 \(k\) 小的排列的权值,对 \(998244353\) 取模。
\(n \le 10^{18}, k \le \min(n!, 10^{18})\)。

首先可以发现,权值最小的排列数量其实是很多的,大概有 \(\prod_{i = 0}^{\left\lfloor \log_2 n \right\rfloor} \left(\lfloor \frac{n}{2^i} \rfloor - \lfloor \frac{n}{2^{i + 1}} \rfloor\right)!\) 个。这个数在 \(n = 29\) 时就超过了 \(10^{18}\),所以当 \(n > 28\) 时,只需考虑求最小的权值;当 \(n \le 28\) 时,可以直接暴力 dp。

Part1 \(n > 28\)

不难发现,权值实际上就是 \(\sum_{i = 1}^n f(p_i) \cdot i \cdot (n - i + 1)\)。

这里有一个贪心就是 \(f(p_i)\) 大的数尽量往两边放,小的尽量往中间放,这个是显然的。

然后去优化这个贪心。考虑先求出每种 \(f(x)\) 的数量,然后一种一种的放。数量是好求的,然后需要求的就是:

\[\sum_{i \in [l, r]} i \cdot (n - i + 1) = (n + 1) \cdot \left(\frac{r(r +1)}{2} - \frac{(l - 1)l}{2}\right) + \left(\frac{r (r + 1)(2r + 1)}{6} + \frac{(l - 1)l(2l - 1)}{6} \right) \]

由于本质不同的 \(f(x)\) 的数量是 \(O(\log n)\) 的,所以这部分时间复杂度是 \(O(\log n)\) 的。

Part2 \(n \le 28\)

这部分大体的思路就是由于最大的权值是 \(6000\) 左右的,所以可以对每种权值求出它对应的排列的个数。

由于本质不同的 \(f(x)\) 至多有 5 个,所以可以设计 dp 为 \(f_{now_1, now_2, now_3, now_4, now_5, val}\) 表示 \(f(x) = i\) 的选了 \(now_i\) 个,权值为 \(val\) 时的方案数,转移就是枚举哪个数多选了一个向后转移。

状态数大概是 \(15 \times 8 \times 5 \times 3 \times 2 \times 5983 = 21538800\)。

constexpr int MAXLG = 60, inv2 = 499122177, inv6 = 166374059;
ll n, k;

namespace Solve1 {
	ll lim[MAXLG];
	
	int calc(ll L, ll R) {
		if (L > R) return 0;
		auto s1 = [&](int n) { return mul(mul(n, n + 1), inv2); };
		auto s2 = [&](int n) { return mul(mul(mul(n, n + 1), 2 * n + 1), inv6); };
		auto sum1 = [&](int l, int r) { return del(s1(r), s1(l - 1)); };
		auto sum2 = [&](int l, int r) { return del(s2(r), s2(l - 1)); };
		int N = (n + 1) % MOD, l = L % MOD, r = R % MOD;
		int ans = del(mul((int)((n + 1) % MOD), sum1(l, r)), sum2(l, r));
		return add(ans, ans);
	}
	
	int Main() {
		for (int i = 0; i <= __lg(n); i ++) lim[i] = (n >> i) - (n >> i + 1);
		int ans = 0; ll pos = 1; bool lst = false;
		for (int i = __lg(n); ~i; i --) {
			int ret = 0; ll cnt = lim[i];
			if (lst) {
				cadd(ret, (int)mul(pos % MOD, (n - pos + 1) % MOD));
				cnt --, pos ++, lst = false;
			}
			if (cnt > 1) {
				cadd(ret, calc(pos, pos + (cnt >> 1) - 1));
				pos += cnt >> 1, cnt &= 1;
			}
			if (cnt) cadd(ret, (int)mul(pos % MOD, (n - pos + 1) % MOD)), lst = true;
			cadd(ans, mul(ret, i + 1));
		}
		Write(ans, '\n');
		return 0;
	}
}

namespace Solve2 {
	int cnt[6], val[30];
	ll f[15][8][5][3][2][5983];
	
	int Main() {
		for (int i = 1; i <= n; i ++)
			cnt[__lg(i & -i) + 1] ++;
		ll ret = 1;
		for (int i = 1; i <= __lg(n) + 1; i ++)
			for (int j = 1; j <= cnt[i]; j ++) ret *= j;
		for (int i = 1; i <= n; i ++)
			val[i] = i * (n - i + 1);
		sort(val + 1, val + n + 1);
		{
			int now[6];
			f[0][0][0][0][0][0] = 1;
			for (now[1] = 0; now[1] <= cnt[1]; now[1] ++) for (now[2] = 0; now[2] <= cnt[2]; now[2] ++)
			for (now[3] = 0; now[3] <= cnt[3]; now[3] ++) for (now[4] = 0; now[4] <= cnt[4]; now[4] ++)
			for (now[5] = 0; now[5] <= cnt[5]; now[5] ++) {
				int sum = 1;
				for (int i = 1; i <= 5; i ++) sum += now[i];
				for (int u = 0; u <= 5982; u ++) {
					for (int i = 1; i <= 5; i ++) if (now[i] < cnt[i]) {
						ll pre = f[now[1]][now[2]][now[3]][now[4]][now[5]][u];
						now[i] ++; int v = u + i * val[sum];
						if (v > 5982) { now[i] --; continue; }
						f[now[1]][now[2]][now[3]][now[4]][now[5]][v] += pre;
						now[i] --;
					}
				}
			}
		}
		for (int i = 0; i <= 5982; i ++) {
			k -= f[cnt[1]][cnt[2]][cnt[3]][cnt[4]][cnt[5]][i] * ret;
			if (k <= 0) { Write(i, '\n'); break; }
		}
		{
			for (int a = 0; a <= cnt[1]; a ++) for (int b = 0; b <= cnt[2]; b ++)
			for (int c = 0; c <= cnt[3]; c ++) for (int d = 0; d <= cnt[4]; d ++)
			for (int e = 0; e <= cnt[5]; e ++) for (int i = 0; i <= 5982; i ++)
				f[a][b][c][d][e][i] = 0;
			for (int i = 1; i <= __lg(n) + 1; i ++) cnt[i] = 0;
		}
		return 0;
	}
}

void slv() {
	n = Read<ll>(), k = Read<ll>();
	if (n > 28) return Solve1::Main(), void();
	else return Solve2::Main(), void();
}

标签:return,int,题解,pos,IV,P9838,权值,mul,now
From: https://www.cnblogs.com/definieren/p/17827304.html

相关文章

  • A Protection Measure-the river chief system
    Theriverandlakechiefsystem,namelytheriverchiefsystem,isanecologicalcivilizationconstructionsysteminnovationinwhichthepartyandgovernmentleadersatalllevelsserveasriverandlakechiefsandareresponsiblefororganizingandlead......
  • 【2023 #84】 锦城ACM周测 (大二个人赛) 题解
    题目难度\(B<D<E=C<A\)A:提高+/省选-B:普及-C:普及/提高-D:普及/提高-E:普及/提高-CandywarQuestion有\(N\)个盒子摆成环形,第\(i\)和盒子里面有\(a_i\)个糖果,他们开始在\(1\)好盒子,然后每个人取一次,可以取\(1\),或者小于当前盒子内糖果数的一个质数\(p\),......
  • chatgpt的api联网报错问题解决:openai公司的api联网报错解决
    chatgpt是啥,这里不讲,openai是啥这里也不讲。要知道我们不论是通过网页web使用chatgpt还是使用api方式通过客户端使用chatgpt都是需要使用外国IP的,     ......
  • 「题解」P6791 [SNOI2020] 取石子
    anti-game没有用,能取到\(n-1\)的必胜,不能取到\(n-1\)的必败,所以现在考虑取走最后石子获胜的情况。对于一个\(n\)来说合法的\(k\)一定是一个前缀,并且一定是贪心取最小的(留给对方的机会更小),所以启发将每个\(n\)最小的合法的\(k=a_n\)打出表来,找到最小的\(j\)满足\(......
  • 【洛谷 P2669】[NOIP2015 普及组] 金币 题解(循环)
    [NOIP2015普及组]金币题目背景NOIP2015普及组T1题目描述国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金币……;这种工资发放模式会一直这......
  • ABC 328 题解
    A直接模拟即可。cin>>n>>m;for(inti=1;i<=n;++i){ intx;cin>>x; if(x<=m)sum+=x;}cout<<sum;B直接模拟即可。intn,ans;boolchk(intx,inty){ intdig=x%10;x/=10; while(x){ if(x%10!=dig)return0; x/=10; } while(y){ if(y%10......
  • 解决 Fedora Live-CD 启动时出现 Invalid image 的问题
    .....安装Fedora39的时候,Live-CD启动报如下错误:InvalidimageFailedtoreadheader:UnsupportedFailedtoloadimage:Unsupportedstart_image()returnedUnsupported尝试了各种解决办法未果,后来在Fedora论坛上发现有人在Fedora37时遇到过同样的问题。......
  • 论文阅读:Active Learning for Point Cloud Semantic Segmentation via Spatial-Struct
    ActiveLearningforPointCloudSemanticSegmentation viaSpatial-StructuralDiversityReasoning通过空间结构多样性推理进行点云语义分割的主动学习摘要众所周知,昂贵的注释成本是点云语义分割技术发展的一个主要制约因素。在本文中,我们提出了一种新的基于主动学习的方法来......
  • ABC328F 题解
    blog。提供一个普通并查集+启发式合并做法。考虑直接维护\(X_i\)。对于\(X_u-X_v=w\),分四种情况。\(X_u,X_v\)都没被维护过。直接钦定\(X_u\getsw,X_v\gets0\),以后再改。\(X_u\)没被维护过,\(X_v\)被维护过。显然\(X_u\getsX_v+w\)。\(X_v\)没被维护过,\(X_u\)被......
  • ABC328G 题解
    blog。剩下几分钟的时候胡出来了,但是时间不够,痛失AK/dk。\(N\le22\),显然状压DP。\(dp_s\)表示确定\(s\)集合的元素所需的代价(这些元素都放在最前面)。确定了\(s\)后,发现会有\(\operatorname{popcount}(s)\)个元素堆在前面,那么枚举\([L,R]\)接在后面,也就是\([\opera......