1 条题解
-
0
自动搬运
来自洛谷,原作者为

樱雪喵
无尽欺骗,无限祈愿 | AFOed | qq: 3480681868搬运于
2025-08-24 22:52:18,当前版本为作者最后更新于2023-11-11 19:45:33,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Part 1.
首先考虑 的时候怎么做。
考虑最终的位置 会被多少个区间统计到,不难发现它计入贡献的次数是 。
也就是说问题转化成,令 ,,我们要对 序列指定一个顺序,最小化 。
这个问题有显然的贪心, 序列升序排序, 序列降序排序,对应位相乘即为最小值。证明是容易的:设 且 ,有 ,移项即得 。Part 2.
在上述过程中发现这么一个问题,方案本质不同的定义是 不相同,但 中有很多数相同。交换它们中的一部分不会使贡献序列改变,但可以使 改变。因此我们估计,在不改变贡献序列的情况下,本质不同的 是相当多的。
考虑估计这个具体数目, 序列中大约有 个 , 个 (暂不讨论上下取整的问题),那么我们至少能搞出 $cnt=\prod\limits_{i=1}^{\log_2 n} (\frac{n}{2^i})! $ 种取到最小值的排列。这里没考虑交换 序列的情况,因此实际值会比这更多。
这说明 的时候答案都可以取到最小值。写个代码求上面的式子,可以发现 的时候 就超过了 。也就是说, 时 为任何值的答案都和 相等。
地模拟上述贪心,配合爆搜可以通过前 个点。有 52pts 暴力分,好良心!
Part 3.
这一部分没有单独的部分分,但我们需要加速贪心的过程。
$$\begin{aligned} s(l,r)&=\sum_{i=l}^r i(n-i+1)\\ &=(n+1)(\sum_{i=l}^r i)-\sum_{i=l}^r i^2\\ &=(n+1)\frac{(l+r)(r-l+1)}{2} - \frac{r(r+1)(2r+1)}{6}+\frac{l(l-1)(2l-1)}{6} \end{aligned} $$
只要快速求出 序列中每个数的数量,就能找到它们在 序列中对应的连续区间。根据基础位运算知识可以得出值为 的出现次数。
令 ,推式子:现在这两个序列都能快速计算,我们可以在 的时间复杂度内处理单次询问。
Part 4.
看起来即使仅考虑本质不同的 序列,这样的数量依然很多。但是我们发现一条关键性质:这种情况下优美度的值域很小,只有 左右。
考虑基于这个进行 dp。进一步观察, 序列中至多有 种不同的值。因为优美度已经计入了状态,我们显然只关心它们填了几个,而不关心它们的位置。故设 表示 分别填了 个,总优美度为 的方案数。
转移时枚举当前位填什么数,式子是容易写出的。这样的有效状态数在 时取到上界,大致有 $16\times 9\times 5\times 3\times 2\times 1.1\times 10^4=4.752\times 10^7$ 种状态。开数组时请注意空间消耗。
结合 Part 3 的做法即可获得 100pts。
#define int long long const int N=1e6+5,mod=998244353; int T,n,k; int f[16][9][5][3][2][11005]; int a[N],b[N],cnt[N],mx=11000,now[6],jc[N]; il int qpow(int n,int k=mod-2) { int res=1; for(;k;n=n*n%mod,k>>=1) if(k&1) res=res*n%mod; return res; } il int S(int x) {x%=mod;return x*(x+1)%mod*(2*x+1)%mod*qpow(6)%mod;} il int get(int l,int r,int n) { if(l>r) return 0; if(r>n/2&&l<=n/2) return (get(l,n/2,n)+get(n/2+1,r,n))%mod; if(l>n/2) l=n-l,r=n-r,swap(l,r); int res=n%mod*((l+r)%mod)%mod*((r-l+1)%mod)%mod*qpow(2)%mod-(S(r)-S(l-1)+mod)%mod; return (res%mod+mod)%mod; } il void solve(int n) { int l=1,r=n,res=0; for(int i=__lg(n)+1;i;i--) { int sum=(n>>i)+(n>>(i-1)&1); int ls=sum/2,rs=sum-ls; if(l<n-r+1) swap(ls,rs); (res+=i*get(l,l+ls-1,n+1)%mod)%=mod,(res+=i*get(r-rs+1,r,n+1)%mod)%=mod; l+=ls,r-=rs; } printf("%lld\n",res); } signed main() { T=read(); jc[0]=1; for(int i=1;i<=15;i++) jc[i]=jc[i-1]*i; while(T--) { n=read(),k=read(); if(n>28) {solve(n);continue;} for(int i=1;i<=n;i++) a[i]=1+__lg(i&(-i)); for(int i=1;i<=n;i++) b[i]=i*(n-i+1); sort(a+1,a+n+1),sort(b+1,b+n+1); for(int i=1;i<=n;i++) cnt[i]=0; for(int i=1;i<=n;i++) cnt[a[i]]++; int Cnt=1; if(n<=28) for(int i=1;i<=__lg(n)+1;i++) Cnt=Cnt*jc[cnt[i]]; 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=0; for(int i=1;i<=5;i++) sum+=now[i]; if(!sum) continue; for(int j=0;j<=mx;j++) { int i=0; for(int k=1;k<=5;k++) { if(now[k]==0) continue; if(j-k*b[sum]<0) continue; now[k]--; i+=f[now[1]][now[2]][now[3]][now[4]][now[5]][j-k*b[sum]]; now[k]++; } f[now[1]][now[2]][now[3]][now[4]][now[5]][j]=i; } } int ans=0; for(int i=0;i<=mx;i++) { ans+=Cnt*f[cnt[1]][cnt[2]][cnt[3]][cnt[4]][cnt[5]][i]; if(ans>=k) {printf("%lld\n",i);break;} } } return 0; }
- 1
信息
- ID
- 9192
- 时间
- 2000~5000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者