2023.8.8

Bronya19C R2.

只因数分解

\(m\) 分拆为不超过 \(n\)\(n!\) 的因数。保证有解。

\(T\le 2\times 10^5\)\(n\le 20\)\(1\le m\le n!\).

令数列 \(\{a\}\)\(\displaystyle a_i=\frac{n!}{i!}(i\in[1,n])\),然后不断与 \(m\) 比对,得到一个 \(cnt\) 数组。容易发现所有的 \(a_i\cdot cnt_i\) 就是一组合法解。

时间复杂度 \(O(Tn)\).

#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define N 25
using namespace std;
int T;ll n,m;
ll fac[N];
ll ans[N];int cnt;
int main(){
	scanf("%d",&T),fac[0]=1;
	for(int i=1;i<=20;i++)
		fac[i]=fac[i-1]*i;
	while(T--){
		scanf("%lld%lld",&n,&m),cnt=0;
		for(int i=1;i<=n&&m;i++){
			if(m>=fac[n]/fac[i]){
				ans[++cnt]=m/(fac[n]/fac[i])*(fac[n]/fac[i]);
				m-=ans[cnt];
			}
		}
		printf("%d\n",cnt);
		for(int i=1;i<=cnt;i++)
			printf("%lld ",ans[i]);
		printf("\n");
	}
	
	return 0;
}

宇宙射线

初始有 \(cnt=ans=0\),对一个排列 \(T_n\) 执行如下操作:

  • 定义 \(T_i\) 的价值为 \(2^{n-T_i}\).

  • 从左往右遍历排列 \(T\),找到最小的 \(i\) 满足 \(\operatorname{val}(T_i)\not=0\),令 \(cnt\leftarrow cnt+1\)\(ans\leftarrow ans+\operatorname{val}(T_i)\)\(\operatorname{val}(T_i)\leftarrow0\).

  • 同时,从左往右遍历所有 \(a_j=cnt\)\(j(j\le m)\),在排列 \(P_j\) 里查找最小的 \(k\) 满足 \(\operatorname{val}(P_{j,k})\not=0\),令 \(\operatorname{val}(P_{j,k})\leftarrow0\).

试找到一个 \(T\) 能够最大化 \(ans\),输出 \(ans\)\(998244353\) 取模的值。

\(3\le n\le 600\)\(1\le m\le \lfloor\frac{n-1}{2}\rfloor\).

保证 \(a\) 数组单调严格递增,且 \(a_1\ge1\)\(a_m\le n-m\).

显然如果当前最高位能够选就一定会在某一个时刻去选。

对于一个当前解 \(S'\),假设新解 \(S=S'\cup i\) 成立,然后枚举每个排列 \(P\) 判断 \(S\) 是否合法即可,矛盾则 \(S=S'\).

假设 \(n,m\) 同阶,时间复杂度 \(O(n^3)\).

#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define N 605
#define P 998244353
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int qpow(int k,int b){
	int ret=1;
	while(b){
		if(b&1)ret=1ll*ret*k%P;
		k=1ll*k*k%P,b>>=1;
	}
	return ret;
}
int n,m,ans;
bool s[N],fl[N],del[N];
int p[N][N],val[N];
bool check(){
	memset(del,0,sizeof(del));
	int cnt=0;
	for(int i=1;i<=n-m;i++){
		if(!fl[i])continue;
		for(int j=1;j<=n;j++){
			if(del[p[i][j]])continue;
			del[p[i][j]]=true;
			if(!s[p[i][j]])break;
			cnt++;
		}
		if(cnt>i)return false;
	}
	return true;
}
int main(){
	n=read(),m=read();
	val[n]=1;
	for(int i=n-1;i;i--)
		val[i]=2*val[i+1]%P;
	for(int i=1;i<=m;i++)
		fl[read()]=true;
	for(int i=1;i<=n-m;i++){
		if(!fl[i])continue;
		for(int j=1;j<=n;j++)
			p[i][j]=read();
	}
	for(int i=1;i<=n;i++){
		s[i]=true;
		if(!check())s[i]=false;
	}
	for(int i=1;i<=n;i++)
		if(s[i])(ans+=val[i])%=P;
	printf("%d\n",ans);
	
	return 0;
}

崩原之战Ⅱ

你玩元神吗。

听说 czz 出了假题。

构造一个无限长的 \(01\)\(S\),对于 \(i\in[1,n]\) 定义

\[b_i=\Big[\forall j\in[l_i,r_i],S_j=c_i\Big] \]

问有多少种不同的 \(\{b\}\).

答案对 \(998244353\) 取模。多测。

\(T\le 5\)\(n\le 10^5\)\(1\le l_i\le r_i\le 10^9\)\(c_i\in\{0,1\}\).

有一个经典假做法就是说 \(f(i)\) 中的方案不一定对 \(f(i+1)\) 有效,也就是 DP 方程假了。这样子几乎拿不到结论分之外的 Subtask 了。

将线段按右端点排序。由大部分假做法的思路,不能单独考虑每条线段,而应考虑颜色相同的一整组线段。

把所选线段分为若干连续的颜色段。

\(f(i,c)\) 表示考虑前 \(i\) 条线段,第 \(i\) 条线段必选且颜色为 \(c\) 的方案数。

枚举上一段的终点 \(j\) 满足 \(c_j\not=c_i\),转移方程为

\[f(i,c)=\sum_{j=0}^{p_i}f(j,1-c)\times2^{g(i-1,r_j+1,c)} \]

其中 \(p_i\) 为满足 \(r_{p_i}<l_i\) 的最大下标,\(g(i-1,r_j+1,c)\) 表示前 \(i-1\) 条线段中左端点 \(\ge r_j+1\) 且颜色为 \(c\) 的线段数量。

也就是说,位于上一段终点右边且颜色和当前线段一样的其他线段随便选。

边界值 \(r_0=0\)\(f(0,0)=f(0,1)=1\).

答案为 \(\displaystyle 1+\sum_{i=1}^{n}f(i,c_i)\).

时间复杂度 \(O(n^2)\),考虑优化。

\(h(i,j,c)=f(j,c)\times2^{g(i,r_j+1,1-c)}\),dp 方程改写为

\[f(i,c)=\sum_{j=0}^{p_i}h(i-1,j,1-c) \]

注意到 \(g\) 的递推关系

\[g(i,r_j+1,c)=\begin{cases}g(i-1,r_j+1,c)+1&j\in[1,p_i],c=c_i\\g(i-1,r_j+1,c)&\text{otherwise.}\end{cases} \]

那么

\[h(i,j,c)=\begin{cases}2h(i-1,j,c)&j\in[1,p_i],c=1-c_i\\h(i-1,j,c)&\text{otherwise.}\end{cases} \]

就是说,处理完第 \(i\) 条线段后,对于所有上一段的终点可以任意选择的线段数量又增加了一条。

使用线段树维护 \(h(i,j,c)\) 的值,支持前缀 \(\times2\),单点赋值,前缀求和,时间复杂度 \(O(n\log n)\).


跳水运动员

为什么题目是这个名字?

我觉得相比图片来说这个超大的 pragma GCC 更好笑。

一棵树,对于 \(x\in[0,k-1]\),问有多少种选择 \(x\) 条边的方案,删边后 \(x+1\) 个联通块的节点编号重排后连续。

答案对 \(998244353\) 取模。

若你帮助阿伟罗解决了问题,阿伟罗会将他的 “罗阳铲” 技术传授给你。

\(n\le 2\times10^5\)\(1\le k\le \min(n,400)\).

Sub1

此时 \(n\le 20\).

暴搜即可,时间复杂度 \(O(2^nn)\).

Sub2

此时 \(n\le 1000\),\(k\le 100\).

把原树拍到序列上,枚举前一个合法转移点,时间复杂度 \(O(n^2k)\).

Sub3

此时树为编号连续的一条链。

所有的转移点都合法,此时可以前缀和优化。

实际上,\(x\) 处的答案显然为 \(\displaystyle\binom{n-1}{x}\).

Sub4

此时以 \(1\) 为根的树是一个小根堆。

考虑只记录连向父亲的边(假设 \(1\) 也有),那么区间 \([l,r]\) 成为连通块合法当且仅当区间内只有一条连向区间外的边。

考虑倒着枚举,每个点的父边都连向比自己编号小的点。

对于 \(i\),设最近一个父亲编号 \(fa_x\)\(i\) 小的点为 \(x\),次近的为 \(y\),那么合法转移点为 \((x,y]\).

维护这个东西可以用 set 搞,时间复杂度 \(O(n\log n+nk)\).

Sub5,6

看不懂先不记了。

posted @ 2023-08-09 08:08  SError  阅读(21)  评论(0编辑  收藏  举报