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)\)暴力,甚至还因为细节挂 ...
随机推荐
- 夏令营501-511NOIP训练17——蛇形矩阵
传送门:QAQQAQ 题意:话说小X在孩提时,都会做标准的蛇形矩阵了,发现很好玩.现在的小X很想对其进行改版,变为如下类型的一个无限大蛇形数阵:令S(x)表示以1为左上角,x为右下角的矩形内所有数之和 ...
- 集中式日志分析平台 - ELK Stack - 安全解决方案 X-Pack
大数据之心 关注 0.6 2017.02.22 15:36* 字数 2158 阅读 16457评论 7喜欢 9 简介 X-Pack 已经作为 Elastic 公司单独的产品线,前身是 Shield, ...
- 编程之法:面试和算法心得(寻找最小的k个数)
内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java 题目描述 输入n个整数,输出其中最小的k个. 分析与解法 解法一 要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个 ...
- (转)打开Mac OSX原生的NTFS功能
xingchongsmbp3:~ xingchong$ xingchongsmbp3:~ xingchong$ xingchongsmbp3:~ xingchong$ sudo ln -s /Volu ...
- Android开发 内存泄露检测框架LeakCanary
前言 挖坑后续填坑 中文网站:https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/ gitbub:https://github.com/squ ...
- Ignite-Spark
2017.12.02 Ignite-Spark 讲ppt的时候还是很紧张 PPT地址 https://files.cnblogs.com/files/swobble/Spark.pptx
- 2.vue插件总结——总有你能用上的插件
UI组件 框架 element - 饿了么出品的Vue2的web UI工具套件 mint-ui - Vue 2的移动UI元素 iview - 基于 Vuejs 的开源 UI 组件库 Keen-UI - ...
- 洛谷 P1941 飞扬的小鸟 (NOIP 2014)
题面 题解 背包细节题,wa了一片,上升的过程我们可以看做一个完全背包(多重背包好像跑不过去),下降 过程是一个0/1背包,为了避免冲突应该先跑多重,先跑0/1就有可能产生这个点又上升又下降的 非法情 ...
- linux 编译安装php选项
PHP安装 ./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-mysql=/u ...
- 深入浅出 Java Concurrency (40): 并发总结 part 4 性能与伸缩性[转]
性能与伸缩性 使用线程的一种说法是为了提高性能.多线程可以使程序充分利用闲置的资源,提高资源的利用率,同时能够并行处理任务,提高系统的响应性. 但是很显然,引入线程的同时也引入了系统的复杂性.另外系统 ...