首页 > 其他分享 >CWOI 2023.05.04 题解

CWOI 2023.05.04 题解

时间:2023-05-05 09:45:56浏览次数:51  
标签:ch frac limits int 题解 sum 2023.05 CWOI 逆序

mzx 的动态规划杂题选讲。sto

ARC153 D - Sum of Sum of Digits

P7152 [USACO20DEC] Bovine Genetics G

CF1542E2 Abnormal Permutation Pairs (hard version)

题意

给定 \(n,m\),求有多少对长度为 \(n\) 的排列 \(p,q\),满足以下条件:

  • \(p\) 的字典序小于 \(q\);

  • \(p\) 的逆序对个数 严格大于 \(q\);

答案对 \(m\) 取模。\(1\le n\le 500\),\(1\le m\le 10^9\),不保证 \(m\) 为素数。

解法

发现这个题需要满足的条件有两个,直接一起做的话很困难,可以先思考满足其中一个条件时怎么做。

发现求的 \(p,q\) 是一个排列,而排列有一个很好的性质:在删去一些数后,因为我们只关心它们的相对大小关系,所以剩下的数也可以看做一个排列,这就是一个子问题了。

  • 只用满足第一个条件:

    • 这个字典序的限制很经典,可以枚举前多少位相同,前面的一样,后面的随便选。
  • 只用满足第二个条件:

    • 定义 \(f_{i,j}\) 表示有多少个 \(i\) 的排列刚好有 \(j\) 个逆序对。

    • 如果你枚举每个位置放哪个数,就需要在状态中记录一个指数级别的维度表示哪些数被选了,这并不利于我们的转移;

    • 考虑把这个看做在一个 \(i-1\) 的排列中插入一个 \(i\)。如果插在第 \(k\) 个数之后,就会增加 \(i-1-k\) 个逆序对。转移就是 \(f_{i,j}=\sum\limits_{k=0}^{i-1}f_{i-1,j-(i-1-k)}\)。这个转移是从连续的一段转过来的,可以前缀和优化,时间复杂度 \(O(n^3)\)。

现在我们同时考虑两个条件。假设 \(p,q\) 的前 \(i-1\) 位相同。此时,排列可以分成三部分:前 \(i-1\) 位,第 \(i\) 位,后 \(n-i\) 位。注意到 \(p,q\) 第一部分完全相同,所以其与二三部分的逆序对数相同,可以忽略。因此,我们需要考虑的逆序对数只由两部分构成:

  • 第 \(i\) 位与后 \(n-i\) 位间的逆序对;

  • 后 \(n-i\) 位中的逆序对;

因为排列的性质,后 \(n-i+1\) 位已经是独立的了。

  • 假设 \(p,q\) 第 i 位填的是 \(k_1,k_2\),则 \(p\) 就比 \(q\) 少了 \(k_2-k_1\) 个逆序对。

  • 令 \(d=k_2-k_1\),因为在取出第 \(i\) 位填的数后后 \(n-i\) 位与 \(i\) 位填的数的值没有关系,而逆序对数量只需要满足 \(p\) 比 \(q\) 多至少 \(d\) 个,我们只用枚举第 \(i\) 位的差值 \(d\)。此时,设 \(p\) 第三部分的逆序对数位 \(j\),则 \(q\) 可选的方案共有 \(\sum\limits_{k=0}^{j-d-1}f_{n-i,k}\) 种。

整理一下,答案就是

\[ans=\sum\limits_{i=1}^{n}A_n^{i-1}\sum\limits_{d=1}^{n-i}(n-i-d+1)\sum\limits_{j=0}^{\frac{(n-i)(n-i-1)}{2}}f_{n-i,j}\sum\limits_{k=0}^{j-d-1}f_{n-i,k} \]

其中 \(A_n^{i-1}\) 表示排列数。

这是一个 \(O(n^6)\) 的柿子,我们考虑优化。记 \(g_{i,j}=\sum\limits_{k=0}^jf_{i,k}\),\(s_{i,j}=\sum\limits_{k=0}^jk\times g_{i,k}\),\(t_{i,j}=\sum\limits_{k=0}^jg_{i,k}\)。

\[\begin{align} ans&=\sum\limits_{i=1}^{n}A_n^{i-1}\sum\limits_{d=1}^{n-i}(n-i-d+1)\sum\limits_{j=0}^{\frac{(n-i)(n-i-1)}{2}}f_{n-i,j}\sum\limits_{k=0}^{j-d-1}f_{n-i,k}\\ &=\sum\limits_{i=1}^{n}A_n^{i-1}\sum\limits_{d=1}^{n-i}(n-i-d+1)\sum\limits_{j=0}^{\frac{(n-i)(n-i-1)}{2}}f_{n-i,j}g_{n-i,j-d-1}\\ &=\sum\limits_{i=1}^{n}A_n^{i-1}\sum\limits_{j=0}^{\frac{(n-i)(n-i-1)}{2}}f_{n-i,j}\sum\limits_{d=1}^{n-i}(n-i-d+1)g_{n-i,j-d-1}\\ &=\sum\limits_{i=1}^{n}A_n^{i-1}\sum\limits_{j=0}^{\frac{(n-i)(n-i-1)}{2}}f_{n-i,j}\sum\limits_{d=1}^{n-i}[(j-d-1)+(n-i-j+2)]g_{n-i,j-d-1}\\ &=\sum\limits_{i=1}^{n}A_n^{i-1}\sum\limits_{j=0}^{\frac{(n-i)(n-i-1)}{2}}(n-i-j+2)f_{n-i,j}\sum\limits_{d=1}^{n-i}g_{n-i,j-d-1}+\sum\limits_{i=1}^{n}A_n^{i-1}\sum\limits_{j=0}^{\frac{(n-i)(n-i-1)}{2}}f_{n-i,j}\sum\limits_{d=1}^{n-i}(j-d-1)g_{n-i,j-d-1}\\ \end{align} \]

发现前面可以用 \(t\) 数组优化,后面可以用 \(s\) 数组优化。总时间复杂度 \(O(n^3)\)。

\(n^3\) 的数组开不下,需要一边求 \(f,g,s,t\) 的同时统计答案。

实际操作时需要考虑越界等无解的情况,处理比较繁琐,建议分段运算。

code:

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,p,ans,dm[505][505],f[200005],g[200005],s[200005],t[200005];
int askG(int l,int r){
	if(l>r)return 0;
	if(r<0)return 0;
	if(l-1<0)return g[r];
	else return (g[r]-g[l-1]+p)%p;
}
int askS(int l,int r){
	if(l>r)return 0;
	if(r<0)return 0;
	if(l-1<0)return s[r];
	else return (s[r]-s[l-1]+p)%p;
}
int askT(int l,int r){
	if(l>r)return 0;
	if(r<0)return 0;
	if(l-1<0)return t[r];
	else return (t[r]-t[l-1]+p)%p;
}
signed main(){
	n=read(),p=read();
	for(int i=0;i<=n;i++){
		dm[i][0]=1;
		for(int j=1;j<=i;j++)dm[i][j]=dm[i][j-1]*(i-j+1)%p;
	}
	f[0]=1,g[0]=1,s[0]=0,t[0]=1;
	for(int i=0;i<=0;i++){
		for(int j=0;j<=i+1;j++){
			ans=(ans+dm[n][n-i-1]*f[j]%p*askS(0,j-2)%p)%p;
		}
		for(int j=0;j<=i+1;j++){
			ans=(ans+dm[n][n-i-1]*(i-j+2)%p*f[j]%p*askT(0,j-2)%p)%p;
		}
		for(int j=i+2;j<=i*(i-1)/2;j++){
			ans=(ans+dm[n][n-i-1]*f[j]%p*askS(-i+j-1,j-2)%p)%p;
		}
		for(int j=i+2;j<=i*(i-1)/2;j++){
			ans=(ans+dm[n][n-i-1]*(i-j+2)%p*f[j]%p*askT(-i+j-1,j-2)%p)%p;
		}		
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i*(i-1)/2;j++){
			f[j]=askG(j-i+1,j);
		}
		g[0]=f[0];
		for(int j=1;j<=(i+1)*((i+1)-1)/2;j++){
			g[j]=(g[j-1]+f[j])%p;
		}
		s[0]=0*g[0];
		for(int j=1;j<=(i+1)*((i+1)-1)/2;j++){
			s[j]=(s[j-1]+j*g[j]%p)%p;
		}
		t[0]=g[0];
		for(int j=1;j<=(i+1)*((i+1)-1)/2;j++){
			t[j]=(t[j-1]+g[j])%p;
		}
		if(i<n){
			for(int j=0;j<=i+1;j++){
				ans=(ans+dm[n][n-i-1]*f[j]%p*askS(0,j-2)%p)%p;
			}
			for(int j=0;j<=i+1;j++){
				ans=(ans+dm[n][n-i-1]*(i-j+2)%p*f[j]%p*askT(0,j-2)%p)%p;
			}
			for(int j=i+2;j<=i*(i-1)/2;j++){
				ans=(ans+dm[n][n-i-1]*f[j]%p*askS(-i+j-1,j-2)%p)%p;
			}
			for(int j=i+2;j<=i*(i-1)/2;j++){
				ans=(ans+dm[n][n-i-1]*(i-j+2)%p*f[j]%p*askT(-i+j-1,j-2)%p)%p;
			}
		}
	}
	printf("%lld\n",(ans+p)%p);
	return 0;
}

总结

  • 对于多条件计数问题,通过分类讨论,枚举等方式降低条件的耦合度,独立解决低耦合度的问题并合并成最终问题的答案;

  • 设计动态规划时要 考虑阶段本身的转移是否容易,本题转化后的基本问题中,如果按下标位置动态规划,阶段转移需要知道前面比该位置小的数的个数,需要记录的状态为指数级。如果按值的大小从小到大动态规划,转移时只需要枚举当前的数放在当前的哪个下标,需要记录的状态数为常数级;

  • 看推柿子的思路;

标签:ch,frac,limits,int,题解,sum,2023.05,CWOI,逆序
From: https://www.cnblogs.com/xx019/p/17373087.html

相关文章

  • 洛谷P7469题解
    题面题意:有两个字符串a和b,问b中有多少个本质不同子串可以由a删除若干个字符得到。|a|,|b|<=3000题解:字典树(这个题做法很多,后补)。把字符串b的每个子串打到字典树上。然后因为3000^2*26这个东西比较大,所以不能用nxt[id][26]来存储,于是使用链式前向星存图,这样tri......
  • 问题解答 | FMCW TDMA-MIMO毫米波雷达信号处理仿真
    本文编辑:@调皮连续波,保持关注调皮哥,获得更多雷达学习资料和建议!大家好,我是调皮哥,今天继续给大家分享干货,助力大家轻松、快乐、有方向地学习雷达。之前分享的文章:雷达仿真|FMCWTDMA-MIMO毫米波雷达信号处理仿真(可修改为DDMA-MIMO)当中,存在几个小问题(bug),具体如下:第十节:多普勒补偿”......
  • 【2023.05.04】幸运的猫(下)
    本次博客主要写黑猫回家后的故事未到家前我打电话和我父亲开玩笑说要带女朋友回家过年我爹还蛮激动的,问是哪里的女孩子,我说是福州的忘记了带回家后他是什么心情了哈哈果然还是要多写日记啊,不然什么都忘记了可太糟糕了初到家中初到家里的时候是还关在笼子里的,因为想把猫养......
  • 关于容斥原理 / P5505题解
    发现很多题解连容斥原理的“钦定”和“至少”的区别都讲不清楚,误导萌新,所以写一下这两个东西的区别“钦定”这个东西是会算重的,而“至少”不会。举个例子吧,比如\(1\2\3\)三个位置不合法,如果我说“钦定”两个位置不合法,那么这里计算方案的时候这个不合法的方案会被计算三次,分......
  • [JOI 2016 Final]断层 题解
    题目链接首先发现斜着平移比较难处理,所以考虑将平面逆时针旋转\(45°\)。接着发现风化也不好处理,但是风化的一定不会作为答案,所以我们可以离线,然后倒着处理操作,上升变为下降。我们发现每个初始\(0\)点最后的坐标就是它正着做时初始的坐标,且每次操作都只会将连续一段点的\(......
  • 【23.05.03】好题题解
    好题题解A题目大意:计算一个项数为\(n\)的多项式除以\(x^3-x\)的余数多项式。数据范围:对于\(100\%\)的数据:\(2\leqn\leq2\times10^5\)解题分析:水题,直接多项式除法模拟即可。需要注意细节。ACCode:#include<bits/stdc++.h>usingnamespacestd;#d......
  • 【题解】ABC300 F,G
    F.MoreHolidays题目分析:考虑刻画一下我们选择是什么样子的。考虑我们最后选择的\(T\)中的一段一定是形如:一个完整的S选择一个后缀\(+\)若干个完整的S\(+\)一个完整的S的前缀。这样的话就启示我们直接枚举这个前后缀选择的是什么,然后就可以很快算出来了,但是枚举前......
  • [POI2005]SAM-Toy Cars 题解(贪心+堆)
    题面首先考虑一个贪心策略:当地板已经放满需要取出一个时,取下一次使用时间\(nxt\)最晚的那个。所以我们只需要一个可以快速求出一个集合中\(nxt\)最小的点并删除,插入新点的数据结构,这里很容易想到堆。代码很简洁,注意数组的下标是位置还是颜色(考场100pts到0pts)。code:......
  • Valhalla Siege 题解
    题目传送门一道二分题。先观察数据范围,\(1\len,q\le200,000\),显然需要\(O(n\logn)\)的复杂度。且\(1\lek_i\le10^{14}\),需要开longlong。我们需要二分到第一个血量大于伤害值的武士的位置,前面的武士都死了。而在C++算法库中,有一个函数upper_bound(底层原理是二分)正......
  • [蓝桥杯 2017 国 C] 合根植物 题解
    题目传送门一道并查集模板题。没什么好说的,先给个并查集模板,神犇可以直接跳过。查找根:intfind_root(intn){if(fa[n]==n)returnn;returnfa[n]=find_root(fa[n]);}合并:voidmerge(intx,inty){intsx=find_root(x),sy=find_root(y);......