2019.10.28 csp-s模拟测试91 反思总结
有一场没一场的233
T1:
胡乱分析一下题意,发现和为n的x个正整数,不同的数字种类不会超过√n个。假设这x个数字都不同,最多也就是(x+1)*x/2=n。
所以可以维护现有的size值以及对应的数目cnt。修改的时候用并查集维护牌堆,然后在储存size值和cnt的数组里暴力进行修改,为了使记录size值的val数组有序,可能需要把数组整体平移的操作,复杂度O(√n)。
询问的时候维护两个指针l,r。r指向与val[l]的差距第一个大于等于c的位置,每次移动l的时候维护r以及r位置及以后的后缀和num,ans+=cnt[l]*num。c=0的时候特殊处理一下。由于使用了双指针,复杂度为O(√n)。
整体复杂度O(√n)。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+;
int n,m,fa[N],siz[N],val[N],cnt[N],tot;
int get(int x){
if(x==fa[x])return x;
else return fa[x]=get(fa[x]);
}
void remove(int x){
int pos=lower_bound(val+,val+tot+,x)-val;
cnt[pos]--;
if(!cnt[pos]){
for(int i=pos;i<tot;i++){
val[i]=val[i+];
cnt[i]=cnt[i+];
}
tot--;
}
}
void add(int x){
int pos=lower_bound(val+,val+tot+,x)-val;
if(val[pos]==x)cnt[pos]++;
else{
for(int i=tot+;i>pos;i--){
val[i]=val[i-];
cnt[i]=cnt[i-];
}
tot++;
val[pos]=x;
cnt[pos]=;
}
}
long long work(int c){
long long ans=;
if(!c){
c++;
long long num=;
int l=;
int r=lower_bound(val+,val+tot+,val[l]+c)-val;
for(int i=r;i<=tot;i++)num+=cnt[i];
while(l<=tot&&r<=tot&&l<=r){
while(val[r]-val[l]<c&&r<=tot){
num-=cnt[r];
r++;
}
if(r>tot)break;
ans+=1ll*cnt[l]*num;
l++;
}
for(int i=;i<=tot;i++){
ans+=(1ll*cnt[i]*(cnt[i]-))/;
}
}
else{
long long num=;
int l=;
int r=lower_bound(val+,val+tot+,val[l]+c)-val;
for(int i=r;i<=tot;i++)num+=cnt[i];
while(l<=tot&&r<=tot&&l<=r){
while(val[r]-val[l]<c&&r<=tot){
num-=cnt[r];
r++;
}
if(r>tot)break;
ans+=1ll*cnt[l]*num;
l++;
}
}
return ans;
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)fa[i]=i,siz[i]=;
fa[n]=n,siz[n]=;
val[++tot]=,cnt[tot]=n;
for(int i=,opt,x,y;i<=m;i++){
scanf("%d",&opt);
if(opt==){
scanf("%d%d",&x,&y);
int x1=get(x),y1=get(y);
if(x1==y1)continue;
fa[y1]=x1;
remove(siz[x1]);
remove(siz[y1]);
add(siz[x1]+siz[y1]);
siz[x1]+=siz[y1];
}
else{
scanf("%d",&x);
printf("%lld\n",work(x));
}
}
return ;
}
T2:
额啊…这题卡了很久才在大佬的帮助下弄出来orz
对于原序列的每一个数字分别考虑,每次只处理指定的这一个数字。设dp[i][j]为在归并排序的第i层,这个数字排在第j位的概率。这个位置j是每一层中在同样的数字里的相对位置,和其它不同的数字的位置无关。dp数组计算出来的概率只影响同种相同的数字,最后计算答案的时候,只需要加上有多少个数字比它小,即相同的这一段数字会排在哪个位置的偏移量。
考虑每一次合并一个包含当前处理数字的区间,设左儿子区间有p个相同的数字,右儿子区间有q个。枚举i和j,设i代表包含目标数字的这段区间,当前要使一边的第i个被放下,另一边已经放了j个。转移方程是dp[dep][i+j]+=dp[dep+1][i]*g[i][j+1]*1/2,因为只有i这一边包含目标数字所以只有dp[dep+1][i]表示目标数字在第dep+1层的i位置的概率,方程的含义即从i位置转移到i+j位置。g[i][j]表示两个指针分别指在i位置和j位置的概率,g数组可以预处理。指针指在某个位置,代表这个位置的数字还没有被选择,指针的每一种指向可以转移到两种后续的指针指向,所以要*1/2。特殊情况,如果j=q,这时j一边的指针已经指向最后一个数字的下一位,即选完j这边的数字,那么转移的时候要将g数组替换成g数组的前缀和sum[i][j],sum[i][j]=g[1][j]+g[2][j]+...+g[i][j]。考虑j一边已经选完以后i的转移,dp[dep][i+j]+=dp[dep+1][i]*g0[i][j+1],这里的g0[i][j+1]表示一边已经选完j个,另一边指向i的概率,g0的一边被卡死了,显然与g的数值不同。g0[i][j+1]=g[i][j]*1/2+g0[i-1][j+1],把后边的g0递推回去,可得等式右边=sum[i][j]*1/2。
使i代表包含目标数字的那段区间,则i从1开始,j从0开始。需要讨论目标数字在左儿子区间还是右儿子区间。当操作区间的左右儿子区间只有一边含有与目标数字相同的数字的时候,相对位置的概率比起深一层是不变的,直接把下一层的dp数组复制上来就好。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=;
int n;
long long sum[][],g[][],tw,dp[][],ans;
int h,f,a[],b[];
long long ks(long long x,int k){
long long num=;
while(k){
if(k&)num=num*x%mod;
x=x*x%mod;
k>>=;
}
return num;
}
void pre(){
for(int i=;i<=n;i++){
for(int j=;j<=n;j++){
if(i==&&j==)g[i][j]=;
else if((i==&&j==)||(i==&&j==)){
g[i][j]=tw;
}
else{
g[i][j]=(g[i-][j]*tw%mod+g[i][j-]*tw%mod)%mod;
}
}
}
for(int i=;i<=n;i++){
for(int j=;j<=n;j++)sum[j][i]=(sum[j-][i]+g[j][i]*tw%mod)%mod;
}
}
int work(int l,int r,int val,int dep){
if(l==r){
if(l==val)dp[dep][]=;
return a[l]==a[val];
}
int mid=(l+r)/;
int p=work(l,mid,val,dep+);
int q=work(mid+,r,val,dep+);
if(l<=val&&val<=r){
if(!p||!q){
for(int i=;i<=p+q;i++)dp[dep][i]=dp[dep+][i];
}
else{
if(val>mid)swap(p,q);
for(int i=;i<=p+q;i++)dp[dep][i]=;
for(int i=;i<=p;i++){
for(int j=;j<=q;j++){
if(j==q){
dp[dep][i+j]=(dp[dep][i+j]+dp[dep+][i]*sum[i][j]%mod)%mod;
}
else dp[dep][i+j]=(dp[dep][i+j]+dp[dep+][i]*g[i][j+]%mod*tw%mod)%mod;
}
}
}
}
return p+q;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
tw=ks(,mod-);
pre();
sort(b+,b+n+);
for(int i=;i<=n;i++){
f=work(,n,i,);
h=lower_bound(b+,b+n+,a[i])-b-;
ans=;
for(int j=;j<=f;j++){
ans=(ans+dp[][j]*(h+j)%mod)%mod;
}
printf("%lld ",ans);
}
return ;
}
T3:
被满足k的最长区间覆盖的点,答案一定是这个最长区间的长度。由此拓展,如果能找到每一个右端点对应的最远左端点,除非这个右端点无解,否则每个点一定都有被覆盖到的机会。从覆盖它的这些区间中选择最长的作为答案即可。
于是考虑枚举右端点。发现从一个右端点往左处理的时候,min-max值是递减的,而or-and值是递增的。把数字都看成二进制,那么在从右往左扫的过程中不同的or值只会有log个,and值也是如此。那么把到右端点的or值和到右端点的and值都相等的节点们看作同一段,用链表维护。每次把所有的or和and值都合并上新的右端点,把新的相同的一段合并起来。因为对于每个右端点要求最远的左端点,所以从左往右扫每个链表节点,如果当前这一段有可能产生满足答案的左端点,由于or-and在同一段中不变,min-max递减,所以二分即可找到最远的左端点。判断这一段是否可能产生左端点以及二分check都要利用st表查询最大最小值。
把所有右端点和对应的最远左端点扔进优先队列q里,左端点为第一关键字。最后查询答案的时候从左往右扫整个序列,如果q中有左端点满足位置的区间,就把它扔进另一个优先队列q1里,r-l+1位第一关键字,r为第二关键字。每次扫到一个新的位置,检查q1的队头的r是否已经不满足位置。最后如果q1里存在区间就输出队头的区间长度,没有就输出-1。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e6+;
int n,k,a[N],sts[N][],stb[N][],top,fir,maxn,cnt=;
struct node{
int l,r,an,o,nxt,lst;
}t[N];
priority_queue<pair<int,int> >q,q1;
int work(int l,int r){
if(l==r)return ;
int minn=,maxx=;
int lens=r-l+,logn;
for(int i=,j=;j<=lens;i++,j<<=)logn=i;
minn=min(sts[l][logn],sts[r-(<<logn)+][logn]);
maxx=max(stb[l][logn],stb[r-(<<logn)+][logn]);
return minn-maxx;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=,j=;j<=n;i++,j<<=)maxn=i;
for(int i=;i<=n;i++)scanf("%d",&a[i]),sts[i][]=stb[i][]=a[i];
for(int i=;i<=maxn;i++){
for(int j=;j+(<<i)-<=n;j++){
sts[j][i]=min(sts[j][i-],sts[j+(<<(i-))][i-]);
stb[j][i]=max(stb[j][i-],stb[j+(<<(i-))][i-]);
}
}
for(int i=;i<=n;i++){
top++;
t[top].l=t[top].r=i;
t[top].lst=top-,t[top].nxt=;
t[top].o=t[top].an=a[i];
t[top-].nxt=top;
int x=top;
while(x){
if(t[x].lst==){
fir=x;
break;
}
t[t[x].lst].an&=a[i],t[t[x].lst].o|=a[i];
if(t[t[x].lst].an==t[x].an&&t[t[x].lst].o==t[x].o){
t[x].l=t[t[x].lst].l;
t[t[t[x].lst].lst].nxt=x;
t[x].lst=t[t[x].lst].lst;
}
else x=t[x].lst;
}
for(int j=fir;j;j=t[j].nxt){
if(t[j].o-t[j].an+work(t[j].r,i)>=k){
int l=t[j].l,r=t[j].r,ans=;
int val=t[j].o-t[j].an;
while(l<=r){
int mid=(l+r)/;
if(val+work(mid,i)>=k){
ans=mid;
r=mid-;
}
else l=mid+;
}
q.push(make_pair(-ans,-i));
break;
}
}
}
for(int i=;i<=n;i++){
while(q.size()&&-q.top().first<=i){
q1.push(make_pair(-q.top().second+q.top().first+,-q.top().second));
q.pop();
}
while(q1.size()&&q1.top().second<i)q1.pop();
if(q1.size())printf("%d ",q1.top().first);
else printf("-1 ");
}
return ;
}
我是个憨憨吧…我st表居然一开始是log级别的查询23333
2019.10.28 csp-s模拟测试91 反思总结的更多相关文章
- 2019.10.28 CSP%您赛第四场t3
我写不动前两个了. 原谅一下. ____________________________________________________________________________________ ...
- csp-s模拟测试91
csp-s模拟测试91 倒悬吃屎的一套题. $T1$认真(?)分析题意发现复杂度不能带$n$(?),计划直接维护答案,考虑操作对答案的影响,未果.突然发现可以动态开点权值线段树打部分分,后来$Tm$一 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组
2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色
2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)
2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...
- 2019.8.14 NOIP模拟测试21 反思总结
模拟测试20的还没改完先咕着 各种细节问题=错失190pts T1大约三分钟搞出了式子,迅速码完,T2写了一半的时候怕最后被卡评测滚去交了,然后右端点没有初始化为n…但是这样还有80pts,而我后来还 ...
- 2019.8.9 NOIP模拟测试15 反思总结
日常爆炸,考得一次比一次差XD 可能还是被身体拖慢了学习的进度吧,虽然按理来说没有影响.大家听的我也听过,大家学的我也没有缺勤多少次. 那么果然还是能力问题吗……? 虽然不愿意承认,但显然就是这样.对 ...
- 2019.8.1 NOIP模拟测试11 反思总结
延迟了一天来补一个反思总结 急匆匆赶回来考试,我们这边大家的状态都稍微有一点差,不过最后的成绩总体来看好像还不错XD 其实这次拿分的大都是暴力[?],除了某些专注于某道题的人以及远程爆踩我们的某学车神 ...
- 2019/10/17 CSP模拟 总结
T1 补票 Ticket 没什么好说的,不讲了 T2 删数字 Number 很后悔的是其实考场上不仅想出了正解的方程,甚至连优化都想到了,却因为码力不足只打了\(O(n^2)\)暴力,甚至还因为细节挂 ...
随机推荐
- 14. TIM of STM32F103ZE
block diagram 14.3.1 Time-base unit 有三个基础的寄存器: 计数寄存器(TIMx_CNT) 预分配寄存器(TIMx_PSC), 自动重载寄存器(TIMx_ARR) 重 ...
- linq to sql any和all的区别
Any说明:用于判断集合中是否有元素满足某一条件:不延迟.(若条件为空,则集合只要不为空就返回True,否则为False).1.简单形式:仅返回没有订单的客户:var q =from c in db. ...
- webpack 简单笔记(一)
安装部分不介绍了 (一)第一个最简单的demo,单入口,单文件 目录结构: webapck.config.js中代码: 'use strict' const path = require('path' ...
- Python学习之文件操作(二)
CSV文件处理 在Python中处理CSV文件可以使用模块csv.有关csv模块的官方资料看这里. 1 读取csv文件 csv.reader(csvfile, dialect='excel', **f ...
- 2019-11-12-浅谈-Windows-桌面端触摸架构演进
title author date CreateTime categories 浅谈 Windows 桌面端触摸架构演进 lindexi 2019-11-12 14:37:31 +0800 2019- ...
- WJMZBMR打osu! / Easy
WJMZBMR打osu! / Easy 有一个由o,x,?组成的长度为n的序列,?等概率变为o,x,定义序列权值为连续o的长度o的平方之和,询问权值的期望, 解 注意到权值不是简单的累加关系,存在平方 ...
- 三. var let const的理解 以及 立即执行函数中的使用 以及 for循环中的例子
一. 立即执行函数 windows中有个name属性,name='' '' var 如果我们用var name 去声明,那就会改变windows中name的值(因为我们不是在函数作用域中声明的,所以会 ...
- 【转帖】WebRTC回声抵消模块简要分析
webrtc 的回声抵消(aec.aecm)算法主要包括以下几个重要模块:回声时延估计:NLMS(归一化最小均方自适应算法):NLP(非线性滤波):CNG(舒适噪声产生).一般经典aec算法还应包括双 ...
- day06 tar命令使用,vim简单操作以及linux开机过程
上节课复习: cat: 查看全部文件内容 head: 从头查看文件内容,默认为前10行 tail: tail -f //动态查看文件是否增加内容 >> 追加 > 覆盖 more: 百 ...
- 容斥原理——hdu2204dfs深搜
/* 枚举素数幂p 然后求k^p<=n 的 k的个数 因为 k^p1*p2==k^p2*p1,所以这两种情况是多算的,所以要进行容斥 减去两个质数幂相乘的,再加上三个质数幂相乘的 因为2*3*5 ...