省选模拟赛乱改
省选模拟赛乱改
好长时间不写博客了,发篇题解证明我还活着。
游戏
给定一个正整数序列,每次在非零的元素中等概率选一个,使其减一,进行
赛时多一个等号保龄了,哈哈哈
考虑状压DP,设
其中组合数是确定使某一个元素变成零的操作位置。对于没有对状态产生贡献的操作,最后要统计一下方案数,包含相对位置和数量,注意边界,跑一遍背包即可。
容易发现这样做是正确的。
总复杂度
排序
考虑一种反人类的排序:
- 从排列的开头开始,判断每一对相邻的数是否大小关系正确。
- 如果存在相邻的一对数大小关系不正确:
把较小的那个数丢到排列开头。回到步骤一。 - 排列有序了,结束。
现在给定步骤二的操作次数
对于一个上升前缀,如果后一个元素不满足单调递增,那么它产生的贡献为
构造有些小清新。
二进制拆分,从低往高位枚举,维护一个栈。若当前为1,将未使用的最小值压入栈,次小值输出;若当前为0,如果栈非空,输出栈顶,否则输出最小值。
证明就是对于每一个1,之前都输出了足够多的小于它的元素,并且以后不会再增加。
送信
诈骗题。
初始时有
随机选择一个信箱,在随机一个方向(左或右),从该信箱出发向该方向移动,遇到第一个空信箱就把信放入,结束。若没有空信箱就把信撕毁。
问所有信都送出的方案数。
考虑看成一个环,就是求不经过第
发现方案数很难做,改为求概率。如果最终的信箱确定,那么这些信箱没有区别。
总方案
逆序对
一个长度为
原题范围
注意到数据范围很小,考虑优化状压DP。
首先,
举例来说,假设我们选择了
假设划分成
将状态压成一个整数即可,会多带一个n,复杂度
CODE
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long ll;
const int N=42;
const int M=5e6+40;
int n,a[N],d[N],c[N],g[N],h[N],w[N][N],siz[N];
ll ans[N][N*N];
struct Hash_Table{
int num[M],maxn;
ll val[M];
void ins(int x,int y,ll z){
if(val[x]){
if(num[x]>y)num[x]=y,val[x]=z;
else if(num[x]==y)val[x]+=z;
}
else {
maxn=max(maxn,x);
num[x]=y;val[x]=z;
}
}
}f[2];
inline void get1(int x,int id){
if(id==0){
memset(g,0,sizeof(int)*(n+1));
return ;
}
for(int i=0;i<siz[id];i++){
g[i]=x%w[id][i];
x/=w[id][i];
}
}
inline int get2(int id){
int res=0;
for(int i=siz[id]-1;i>=0;i--){
res=res*w[id][i]+h[i];
}
return res;
}
inline void get3(int len,int y){
for(int i=0,j=0;i<len;i++){
if(i==y)h[j-1]+=g[i];
else h[j++]=g[i];
}
}
signed main()
{
// freopen("q.in","r",stdin);
// freopen("q.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
memcpy(c,a,sizeof(c));
sort(c+i+1,c+n+1);
if(i+1<=n)w[i][siz[i]++]=c[i+1];
for(int j=i+2;j<=n;j++)w[i][siz[i]++]=c[j]-c[j-1];
if(i==n)w[i][siz[i]++]=n+1;
else w[i][siz[i]++]=n-c[n]+1;
}
for(int i=n;i>=1;i--){
d[i]=1;
for(int j=i+1;j<=n;j++){
if(a[j]<a[i])d[i]++;
}
}
f[0].ins(0,0,1);
for(int i=1,flag=1;i<=n;i++,flag^=1){
for(int it=0;it<=f[flag^1].maxn;it++){
if(f[flag^1].val[it]){
get1(it,i-1);
int c=f[flag^1].num[it],s=0;
for(int j=d[i];j<n-i+2;j++)s+=g[j];
get3(n-i+2,d[i]);
f[flag].ins(get2(i),c,f[flag^1].val[it]);
h[d[i]-1]++;
f[flag].ins(get2(i),c+s,f[flag^1].val[it]);
f[flag^1].num[it]=f[flag^1].val[it]=0;
}
}
f[flag^1].maxn=0;
}
for(int it=0;it<=f[n&1].maxn;it++){
if(f[n&1].val[it]){
get1(it,n);
int s=0,c=f[n&1].num[it];
s+=g[0];
ans[s][c]+=f[n&1].val[it];
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=i*i;j++){
if(ans[i][j]){
printf("%d %lld\n",j,ans[i][j]);
break;
}
}
}
}
发烧
给定
SA都快忘干净了。
考虑把所有字符串拼在一起,中间插入互不相同的特殊字符。
对于每个后缀二分答案,判断一个答案是否合法,就是在一个区间内数颜色,判断颜色数是否不小于
使用双指针似乎可以做到
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2024-02-22 牛吃草 题解
2024-02-22 2024初三集训模拟测试2