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\}\).
答案对 \(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\),转移方程为
其中 \(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 方程改写为
注意到 \(g\) 的递推关系
那么
就是说,处理完第 \(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
看不懂先不记了。