6.11 NOI 模拟
\(T1\)魔法师
\(f(x)\)是各个数位之积,当\(f(x)\ne 0\),每一位只能是\(1\sim 9\),考虑数位积的质因数分解只能是\(2,3,5,7\)的形式,考虑对所有的\((a,b,c,d)\)计算满足\(f(x)=2^a\times 3^b\times 5^c\times 7^d\)的\(x\)的数量
这个东西考虑用数位\(dp\)求
\(f[i][0/1][a][b][c][d]\)表示当前已经处理到从高到低第\(i\)为,前\(i\)位的拆分为\(2^a\times 3^b\times 5^c\times 7^d\)的方案数
我们现在得到了\(g[a][b][c][d]\)即满足小于等于\(n\)的所有拆分方案
我们做一个\(Min\)卷积,求一下方案数即可
\(h[a][b][c][d]=\sum g[x_1][x_2][x_3][x_4]\times g[y_1][y_2][y_3][y_4][\min(x_1,y_1)=a,\min(x_2,y_2)=b,\min(x_3,y_3)=c,\min(x_4,y_4)=d]\)
这个过程可以用四维后缀和优化
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int la=60,lb=38,lc=25,ld=25,mod=998244353;
int dp[22][2][64][43][28][28],f[16][65][43][28][28];
ll Ans,n,k;
int sola(int x)
{
if(x==2) return 1;
if(x==4) return 2;
if(x==6) return 1;
if(x==8) return 3;
return 0;
}
int solb(int x)
{
if(x==3) return 1;
if(x==6) return 1;
if(x==9) return 2;
return 0;
}
int solc(int x)
{
if(x==5) return 1;
return 0;
}
int sold(int x)
{
if(x==7) return 1;
return 0;
}
void Init(ll num)
{
int cnt=0,wei[50];
while(num)
{
wei[++cnt]=num%10;
num/=10;
}
reverse(wei+1,wei+1+cnt);
dp[0][1][0][0][0][0]=1;
for(int i=1;i<=cnt;i++)
{
dp[i][0][0][0][0][0]=1;
for(int j=1;j<=9;j++)
{
int ja=sola(j),jb=solb(j),jc=solc(j),jd=sold(j);
for(int a=0;a+ja<=la;a++)
for(int b=0;b+jb<=lb;b++)
for(int c=0;c+jc<=lc;c++)
for(int d=0;d+jd<=ld;d++)
dp[i][0][a+ja][b+jb][c+jc][d+jd]=(1ll*dp[i][0][a+ja][b+jb][c+jc][d+jd]+1ll*dp[i-1][0][a][b][c][d])%mod;
}
for(int j=1,opt=(j==wei[i]);j<=wei[i];j++,opt=(j==wei[i]))
{
int ja=sola(j),jb=solb(j),jc=solc(j),jd=sold(j);
for(int a=0;a+ja<=la;a++)
for(int b=0;b+jb<=lb;b++)
for(int c=0;c+jc<=lc;c++)
for(int d=0;d+jd<=ld;d++)
dp[i][opt][a+ja][b+jb][c+jc][d+jd]=(1ll*dp[i][opt][a+ja][b+jb][c+jc][d+jd]+1ll*dp[i-1][1][a][b][c][d])%mod;
}
}
for(int i=0;i<16;i++)
for(int a=0;a<=la;a++)
for(int b=0;b<=lb;b++)
for(int c=0;c<=lc;c++)
for(int d=0;d<=ld;d++)
{
f[i][a][b][c][d]=(1ll*dp[cnt][0][a][b][c][d]+1ll*dp[cnt][1][a][b][c][d])%mod;
}
for(int i=0;i<16;i++) f[i][0][0][0][0]--;
}
signed main()
{
scanf("%lld%lld",&n,&k);
Init(n);
for(int i=0;i<16;i++)
{
if(i&1)
for(int a=la;a>=0;a--)
for(int b=lb;b>=0;b--)
for(int c=lc;c>=0;c--)
for(int d=ld;d>=0;d--)
f[i][a][b][c][d]=(1ll*f[i][a][b][c][d]+1ll*f[i][a+1][b][c][d])%mod;
if(i&2)
for(int a=la;a>=0;a--)
for(int b=lb;b>=0;b--)
for(int c=lc;c>=0;c--)
for(int d=ld;d>=0;d--)
f[i][a][b][c][d]=(1ll*f[i][a][b][c][d]+1ll*f[i][a][b+1][c][d])%mod;
if(i&4)
for(int a=la;a>=0;a--)
for(int b=lb;b>=0;b--)
for(int c=lc;c>=0;c--)
for(int d=ld;d>=0;d--)
f[i][a][b][c][d]=(1ll*f[i][a][b][c][d]+1ll*f[i][a][b][c+1][d])%mod;
if(i&8)
for(int a=la;a>=0;a--)
for(int b=lb;b>=0;b--)
for(int c=lc;c>=0;c--)
for(int d=ld;d>=0;d--)
f[i][a][b][c][d]=(1ll*f[i][a][b][c][d]+1ll*f[i][a][b][c][d+1])%mod;
}
for(ll i2=0,now2=k;now2;i2++,now2/=2)
for(ll i3=0,now3=now2;now3;i3++,now3/=3)
for(ll i5=0,now5=now3;now5;i5++,now5/=5)
for(ll i7=0,now7=now5,res=0;now7;i7++,now7/=7)
{
for(int i=0;i<16;i++)
for(int j=0;j<16;j++)
if(((i^15)|(j^15))==15) Ans=(1ll*Ans+1ll*f[i][i2+(i&1)][i3+((i&2)!=0)][i5+((i&4)!=0)][i7+((i&8)!=0)]*f[j][i2+(j&1)][i3+((j&2)!=0)][i5+((j&4)!=0)][i7+((j&8)!=0)]%mod)%mod;
}
cout<<Ans<<"\n";
}
\(T2\ Warrior\)
首先当 \(l\) 确定的时候,\(r\) 越大字典序越小,考虑尺取法,每次移动时候判断有多少字典序小,每次查询\(O(n\log n)\),总复杂度是\(O(n^2\log n)\)
考虑如何在线段树上查询,考虑完全包含进去的字典序必然大。
暴力尺取
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define MAXN 1000005
using namespace std;
int w[MAXN],b[MAXN],tot,n,k;
map<int,int>mp;
namespace Seg
{
//支持动态插入值
//然后再这棵线段树上继续尺取找比他小的
#define ls (now<<1)
#define rs ((now<<1)|1)
struct Tr
{
int l,r,num;
bool del;
}tr[MAXN<<2];
void build(int now,int l,int r)
{
tr[now].l=l,tr[now].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
void push_up(int now)
{
tr[now].del=(tr[ls].del|tr[rs].del);
}
void change(int now,int poz,int k)
{
if(poz==0) return ;
if(tr[now].l==poz&&tr[now].r==poz)
{
tr[now].num+=k;
if(tr[now].num) tr[now].del=true;
else tr[now].del=false;
return ;
}
int mid=(tr[now].l+tr[now].r)>>1;
if(poz<=mid)change(ls,poz,k);
else change(rs,poz,k);
push_up(now);
}
bool query(int now)
{
if(tr[now].l==tr[now].r)
{
if(tr[now].num>0) return true;
return false;
}
if(tr[ls].del) return query(ls);
return query(rs);
}
}
int Min_num(int l,int r)
{
int res=0,i,j;
//计算比他小的
// cout<<"Sit: "<<l<<" "<<r<<"\n";
for(i=1,j=0;i<=n;i++)
{
while(Seg::query(1)&&j<=n)
{
Seg::change(1,w[++j],-1);
}
res+=(n-j+1);
// if(l==5&&r==30)cout<<"sol: "<<i<<" "<<j<<"\n";
Seg::change(1,w[i],1);
}
for(int poi=i;poi<=j;poi++)
{
Seg::change(1,w[poi],1);
}
// cout<<"res: "<<res<<"\n";
return res;
}
int bc[MAXN];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
b[i]=w[i];
}
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
{
if(!mp[b[i]]) mp[b[i]]=++tot,bc[tot]=b[i];
}
Seg::build(1,1,tot);
for(int i=1;i<=n;i++) w[i]=mp[w[i]];
for(int l=1,r=0;l<=n;l++)
{
//尺取,往右走直到字典序小于k
while(r<n)
{
Seg::change(1,w[++r],1);
if(Min_num(l,r)<k)
{
Seg::change(1,w[r--],-1);
break;
}
else if(Min_num(l,r)==k) break;
}
if(Min_num(l,r)==k)
{
// cout<<l<<" "<<r<<"\n";
map<int,int>t;
for(int i=l;i<=r;i++)
{
t[bc[w[i]]]++;
}
for(map<int,int>::iterator it=t.begin();it!=t.end();it++)
{
for(int i=1;i<=it->second;i++)
{
cout<<it->first<<" ";
}
}
exit(0);
}
Seg::change(1,w[l],-1);
if(Min_num(l+1,r)==k)
{
map<int,int>t;
for(int i=l+1;i<=r;i++)
{
t[bc[w[i]]]++;
}
for(map<int,int>::iterator it=t.begin();it!=t.end();it++)
{
for(int i=1;i<=it->second;i++)
{
cout<<it->first<<" ";
}
}
exit(0);
}
}
}
考虑在这个基础上加一些随机化,考虑每次随机一个位置,然后如果小于\(k\)的话,就把小于\(k\)的删去,更新一下\(k\),期望下\(O(\log n)\)只剩下一个,单次查询\(O(n\log n)\),总复杂度\(O(n\log^2 n)\)
#include<bits/stdc++.h>
#define rint register int
#define mid ((l+r)>>1)
#define ll long long
#define M 131072
using namespace std;
int Rand(int l,int r)
{
return rand()*rand()%(r-l+1)+l;
}
int T=256,n,k,s[1000001],L[1000001],R[1000001],lim[1000001];
int tr[1000001],cntr[1000001];
void build()
{
for(int i=1;i<=n;++i)
{
if(cntr[i]>0) tr[i+M]=1;
if(cntr[i]<0) tr[i+M]=-1;
if(cntr[i]==0) tr[i+M]=0;
}
for(int i=M-1;i>=1;--i) tr[i]=tr[i<<1]?tr[i<<1]:tr[i<<1|1];
}
void change(int p,int v)
{
int now=p+M;
cntr[p]+=v;
if(cntr[p]>0) tr[now]=1;
if(cntr[p]<0) tr[now]=-1;
if(cntr[p]==0) tr[now]=0;
p=now;
p=(p>>1);
while(p) tr[p]=tr[p<<1]?tr[p<<1]:tr[p<<1|1],p=(p>>1);
}
signed main()
{
double t=clock();
srand(20050323);
scanf("%d %d",&n,&k);
for(rint i=1;i<=n;++i) scanf("%d",&s[i]);
for(rint i=1;i<=n;++i) L[i]=i,R[i]=n;
while(clock()-t<=1400000&&T--)
{
ll now=0;
for(rint i=1;i<=n;++i) now+=R[i]-L[i]+1;
now=Rand(0,now-1);
for(rint i=1;i<=n;++i) cntr[i]=0;
rint nowl,nowr;
for(rint i=1;i<=n;++i)
{
if(L[i]>R[i]) continue;
if(now>=R[i]-L[i]+1) now-=R[i]-L[i]+1;
else
{
nowl=i,nowr=L[i]+now;
break;
}
}
for(rint i=nowl;i<=nowr;++i) ++cntr[s[i]];
build();
ll tot=0;
for(rint l=1,r=0;l<=n;++l)
{
while(r<=n&&(r<l-1||tr[1]>=0))
{
if(++r<=n)
{
change(s[r],-1);
}
}
tot+=n-r+1;
lim[l]=r-1;
change(s[l],1);
}
if(tot<k) for(rint i=1;i<=n;++i) R[i]=min(R[i],lim[i]);
else for(rint i=1;i<=n;++i) L[i]=max(L[i],lim[i]+1);
}
vector<int> res;
for(int i=1;i<=n;++i)
if(L[i]<=R[i])
{
for(rint j=i;j<=L[i];++j) res.push_back(s[j]);
sort(res.begin(),res.end());
for(rint i=0;i<res.size();++i) printf("%d ",res[i]);
exit(0);
}
}
\(T3\)最佳观影
考虑分成两种情况
一种是没有放人的行,另一种是已经放人的行,首先放人的行左右两边可以分开维护
我们每次需要查询人数\(\times\)右端点最小的,考虑用线段树维护一下到边界的人数,在满足条件里选一个最小的就好了,考虑维护二元组\((r,r+d)\),由于我们贡献是\(Mid-r+Mid-d\)
第一维建立线段树,第二维在叶子上维护一个堆,就是查区间最大了
#include<bits/stdc++.h>
using namespace std;
int n,k,x1;
struct node{
int x,y,z;
friend bool operator < (node a,node b)
{
if(a.z!=b.z) return a.z>b.z;
if(a.x!=b.x) return a.x>b.x;
return a.y>b.y;
}
}tr[600100];
priority_queue<node> q[150010];
void build(int x,int l,int r)
{
tr[x].x=0,tr[x].y=0,tr[x].z=1<<30;
if(l==r) return;
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
}
long long cal(int l,int r)
{
return 1ll*(l+r)*(r-l+1)>>1;
}
long long calc(node a,int b)
{
if((a.y<k&&a.y-b<0)||(a.y>k&&a.y+b>k+k))return 1ll<<60;
return 1ll*b*abs(a.x-k)+cal(abs(k-a.y),abs(k-a.y)+b-1);
}
long long calc(int a,int b)
{
if(!a||a==k+k)return 1ll<<60;
return 1ll*b*abs(a-k)+cal(1,b-1>>1)+cal(1,b>>1);
}
void ins(int x,int l,int r,int p,node v)
{
if(l==r)
{
q[l].push(v);
tr[x]=q[l].top();
return;
}
int mid=(l+r)>>1;
if(p<=mid) ins(x<<1,l,mid,p,v);
else ins(x<<1|1,mid+1,r,p,v);
tr[x]=max(tr[x<<1],tr[x<<1|1]);
}
void erase(int x,int l,int r,int pos)
{
if(l==r)
{
q[l].pop();
tr[x]=q[l].size()?q[l].top():(node){0,0,1<<30};
return;
}
int mid=(l+r)>>1;
if(pos<=mid) erase(x<<1,l,mid,pos);
else erase(x<<1|1,mid+1,r,pos);
tr[x]=max(tr[x<<1],tr[x<<1|1]);
}
node query(int x,int l,int r,int pos)
{
if(l>=pos)
{
return tr[x];
}
int mid=(l+r)>>1;
if(pos>mid) return query(x<<1|1,mid+1,r,pos);
return max(query(x<<1,l,mid,pos),query(x<<1|1,mid+1,r,pos));
}
void slo(int a,int b)
{
cout<<a<<" "<<k-(b>>1)<<" "<<k+(b-1>>1)<<"\n";
if((b>>1)+1<k)
{
ins(1,1,k,k-1-(b>>1),(node){a,k-1-(b>>1),abs(a-k)+1+(b>>1)});
}
if((b-1>>1)+1<k)
{
ins(1,1,k,k-1-(b-1>>1),(node){a,k+1+(b-1>>1),abs(a-k)+1+(b-1>>1)});
}
}
int main()
{
scanf("%d%d%d",&n,&k,&x1);
k=(k+1)>>1;
build(1,1,k);
slo(k,x1);
int L=k-1,R=k+1;
for(int i=2,x;i<=n;i++)
{
scanf("%d",&x);
node now=x<=k?query(1,1,k,x):(node){0,0,1<<30};
if(calc(L,x)<=min(calc(now,x),calc(R,x)))
{
if(!L) cout<<"-1\n";
else slo(L--,x);
}
else
{
if(calc(R,x)<calc(now,x))
{
slo(R++,x);
}
else
{
// cout<<1<<endl;
if(now.y<k)
{
erase(1,1,k,now.y);
cout<<now.x<<" "<<now.y-x+1<<" "<<now.y<<"\n";
now.y-=x,now.z+=x;
if(now.y) ins(1,1,k,now.y,now);
}
else
{
erase(1,1,k,k+k-now.y);
cout<<now.x<<" "<<now.y<<" "<<now.y+x-1<<"\n";
now.y+=x,now.z+=x;
if(now.y<k+k) ins(1,1,k,k+k-now.y,now);
}
}
}
}
}
6.11 NOI 模拟的更多相关文章
- 7.11 NOI模拟赛 qiqi20021026的T1 四个指针莫队 trie树
LINK:qiqi20021026的T1 考场上只拿到了50分的\(nq\)暴力. 考虑一个区间和一个区间配对怎么做 二分图最大带权匹配复杂度太高. 先考虑LCS的问题 常见解决方法是后缀数组/tri ...
- 7.11 NOI模拟赛 graph 生成函数 dp 多项式
LINK:graph HDU题库里的原题 没做过自闭. 考虑dp 设\(f_{i,j}\)表示前i个点构成j个联通块是树的方案数. 对于一次询问答案即为\(\sum_{j}f_{n,j}j^k\) 考 ...
- 9.11 myl模拟赛
9.11 myl 模拟赛 100 + 100 + 0 第一题耗费了太多的时间,导致最后一题没有时间想,直接去写了暴力,而且出题人没有给暴力分.... Problem 1. superman [题目描述 ...
- 5.30 NOI 模拟
$5.30\ NOI $模拟 高三大哥最后一次模拟考了,祝他们好运 \(T1\)装箱游戏 显然可以将四种字母之间的空缺当做状态枚举 那么这道题就很显然了 #include<bits/stdc++ ...
- 5.23 NOI 模拟
$5.23\ NOI $模拟 \(T1\)简单的计算几何题 \(zjr:\)我当时没改,那么自己看题解吧 倒是有个简单的随机化方法(能获得\(72pts,\)正确性未知)\(:\) 随机两条切椭圆的平 ...
- 5.6 NOI模拟
\(5.6\ NOI\)模拟 明天就母亲节了,给家里打了个电话(\(lj\ hsez\)断我电话的电,在宿舍打不了,只能用教练手机打了) 其实我不是很能看到自己的\(future,\)甚至看不到高三的 ...
- 5.4 NOI模拟
\(5.4\ NOI\)模拟 \(T1\) 想到分讨,但是暴力输出一下方案之后有很多特别的情况要讨论,就弃了... 假设\(a\)是原序列,\(b\)是我们得到的序列 设\(i\)是最长公共前缀,\( ...
- 11.12模拟考T1(可持续优化)PS:神奇的东西
1.数列操作 (array.pas/c/cpp) [问题描述] 现在有一个数列,最初包含0个数.现在要对数列操作n次,操作有3类. 1) a k,在数列的最后插入一个整数k 2) s 将最近插入的 ...
- NOI模拟赛 Day1
[考完试不想说话系列] 他们都会做呢QAQ 我毛线也不会呢QAQ 悲伤ING 考试问题: 1.感觉不是很清醒,有点困╯﹏╰ 2.为啥总不按照计划来!!! 3.脑洞在哪里 4.把模拟赛当作真正的比赛,紧 ...
随机推荐
- 浅析kubernetes中client-go Informer
之前了解了client-go中的架构设计,也就是 tools/cache 下面的一些概念,那么下面将对informer进行分析 Controller 在client-go informer架构中存在一 ...
- 参与 2022 第二季度 Flutter 开发者调查
2022 Google I/O 大会正式落下帷幕,Flutter 作为 14 个开发者产品和平台中的一款,吸引了来自全球的很多开发者们的关注.随着全国很多地方已经进入夏季,Flutter 今年第二季度 ...
- VS Code - Vim 插件自动切换输入法
前言: 在使用 Linux 的过程中,vim 是一个不错的编辑器,以至于多数人将其用成了习惯,在没有 vim 的环境下还是习惯用 vim 的快捷键来编辑文本.所以便有开发者们为众多的 IDE 和文本编 ...
- LightGBM原理与实践简记
写在前面: LightGBM 用了很久了,但是一直没有对其进行总结,本文从 LightGBM 的使用.原理及参数调优三个方面进行简要梳理. 目录 开箱即用 quickstart sklearn 接口 ...
- 全新升级的AOP框架Dora.Interception[3]: 基于特性标注的拦截器注册方式
在Dora.Interception(github地址,觉得不错不妨给一颗星)中按照约定方式定义的拦截器可以采用多种方式注册到目标方法上.本篇文章介绍最常用的基于"特性标注"的拦截 ...
- AspNetCore&云效Flow持续集成
如今有了越来越多的持续集成工具,给的个人开发者的福利也是很足了,如无必要,自建工具有时只是作为练手了. 众多持续集成工具 现在可用的持续集成工具繁多,各大云服务商都推出了持续集成,甚至是一定条件内都是 ...
- Docker 与 K8S学习笔记(二十五)—— Pod的各种调度策略(上)
上一篇,我们学习了各种工作负载的使用,工作负载它会自动帮我们完成Pod的调度和部署,但有时我们需要自己定义Pod的调度策略,这个时候该怎么办呢?今天我们就来看一下如何定义Pod调度策略. 一.Node ...
- SpringBoot配置多环境下的properties配置文件
1.新建SpringBoot项目之后,再另外创建两个properties文件 2.配置详情 主文件 dev和test文件 两者只是里面的配置信息有所不同而已,比如mysql, redis, nacos ...
- POI 给单元格添加批注
图中红框框是处理单元格内容和批注的地方. 参考:https://blog.csdn.net/qq_38974638/article/details/114837631 //SXSSFWorkbook ...
- NC24724 [USACO 2010 Feb S]Chocolate Eating
NC24724 [USACO 2010 Feb S]Chocolate Eating 题目 题目描述 Bessie has received \(N (1 <= N <= 50,000)\ ...