[29] CSP模拟2
A.不相邻集合
考虑值域上连续的段,可以发现连续的长度为 \(x\) 的段的贡献必定为 \(\lceil{\frac{x}{2}}\rceil\)
考虑并查集维护值域连续的段的大小,每次询问求出全部连续段的 \(\lceil{\frac{size}{2}}\rceil\) 之和即为答案
合并操作:在值域上加入 \(x\),尝试合并 \(x-1\) 与 \(x+1\)
复杂度不对,考虑优化
- 记一个关于值域的 \(vis\),若单次插入不改变 \(vis\),则答案不变
- 每次在并查集上合并时直接统计答案,先减去两个分别的答案,再加上总和的答案
- 记得新加入单点时的贡献为 \(\lceil{\frac{1}{2}}\rceil=1\)
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define void inline void
// #define ONLINE_JUDGE
#ifndef ONLINE_JUDGE
#define test(i) cout<<"test: "<<i<<endl
#define testp(i,j) cout<<i<<" "<<j<<endl
#define testd(i) cout<<i<<" "
#define end cout<<"\n"
#define div <<" "<<
#else
#define test(i)
#define testp(i,j)
#define testd(i)
#define end false
#define div ,
#endif
template<typename T>
void read(T& x){
x=0;bool sym=0;char c=getchar();
while(!isdigit(c)){sym^=(c=='-');c=getchar();}
while(isdigit(c)){x=x*10+c-48;c=getchar();}
if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
read(x);read(args...);
}
vector<int>has;
int ans=0;
class dsu{
public:
int fa[500001],siz[500001];
void reset(int n){
for(int i=1;i<=n;++i){
fa[i]=i;
siz[i]=1;
}
}
int find(int id){
if(fa[id]==id) return id;
fa[id]=find(fa[id]);
return fa[id];
}
void connect(int x,int y){
int fx=find(x),fy=find(y);
if(fx!=fy){
fa[fx]=fy;
ans-=(siz[fy]+1)/2+(siz[fx]+1)/2;
siz[fy]+=siz[fx];
ans+=(siz[fy]+1)/2;
}
}
};
dsu d;
int a[300001];
bool vis[500001];
int tot,lastans=0;
int main(){
int n;
read(n);d.reset(500000);
for(int i=1;i<=n;++i){
read(a[i]);
if(!vis[a[i]]){
vis[a[i]]=true;
has.push_back(a[i]);
ans++;
if(vis[a[i]-1]){
d.connect(a[i],a[i]-1);
}
if(vis[a[i]+1]){
d.connect(a[i],a[i]+1);
}
}
cout<<ans<<' ';
}
}
B.线段树
设 \(f(n,x)\) 表示对从长度为 \(n\) 的连续段建立线段树(即有 \(n\) 个叶节点),根节点的值为 \(x\),左儿子的值为 \(2x\),右儿子的值为 \(2x+1\) 的情况下的全部节点权值和
可以发现对于根节点儿子的权值,相对于根节点权值的变化只有两种:即乘以一个系数或者加上若干值,也即 \(f(n,x)\) 是关于 \(x\) 的一次函数
设 \(f(n,x)=k_{n}x+b_{n}\),考虑到我们在对此线段树向下分治的时候,总会有 \(\lceil{\frac{n}{2}}\rceil\) 长度的区间被分配到左儿子,\(\lfloor{\frac{n}{2}}\rfloor\) 长度的区间被分配到右儿子,也就是说:
\]
根据上设,可得
\]
解这个方程,得到
\]
关于这个递推式的初态,可以感性理解
- 当 \(n=1\) 时,线段树中只有一个节点 \(x\),因此 \(k_{1}=1,b_{1}=0\)
- 当 \(n=2\) 时,线段树中只有三个节点 \(x,2x,2x+1\),和为 \(5x+1\),因此 \(k_{1}=5,b_{1}=1\)
其余开 map 记搜即可
如果你们 WA60 也有可能是 build() 里的 id 忘记取模了
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
template<typename T>
inline T read(){
T x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}
template<typename T>
inline T read(T&a){
T x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return a=x*f;
}
template<typename T>
inline void write(T x){
if(x<0)x*=-1,putchar('-');
if(x>9)write(x/10);
putchar(x%10+'0');
return;
}
const int p=1e9+7;
map<int,int> K,B;
int k(int n){
if(n==0) return 0;
if(n==1) return 1;
if(n==2) return 5;
if(K.count(n)) return K[n];
return K[n]=(2*(k((n+1)/2)+k(n/2))+1)%p;
}
int b(int n){
if(n==0) return 0;
if(n==1) return 0;
if(n==2) return 1;
if(B.count(n)) return B[n];
return B[n]=(b((n+1)/2)+b(n/2)+k(n/2))%p;
}
int build(int id,int l,int r,int L,int R){
if(l>R or r<L) return 0;
if(L<=l and r<=R){
return (k(r-l+1)*id%p+b(r-l+1))%p;
}
int mid=(l+r)/2;
return (build((id*2)%p,l,mid,L,R)+build((id*2+1)%p,mid+1,r,L,R))%p;
}
signed main(){
int T;read(T);
while(T--){
int n,l,r;
read(n);read(l);read(r);
write(build(1,1,n,l,r)%p);putchar('\n');
}
}
C.魔法师
题目需要我们求 \(\max(a_{i}+a_{j},b_{i}+b_{j})\)
分类讨论,注意到 \(a_{i}+a_{j}\gt b_{i}+b_{j}\) 的时候,只需要令 \(a_{i}-b_{i}\gt b_{j}-a_{j}\) 即可,此时的答案即为 \(a_{i}+a_{j}\),否则,当 \(a_{i}-b_{i}\le b_{j}-a_{j}\) 时,答案便为 \(b_{i}+b_{j}\),这样我们就去除了 \(\max\) 符号
因此我们尝试对每个 \(u_{i}=a_{i}-b_{i},v_{i}=b_{i}-a_{i}\) 进行比较. 现在我们可以把问题转化为求满足 \(u_{i}\gt v_{j}\) 的 \((i,j)\) 的 \(a_{i}+a_{j}\) 最小值,或者满足 \(u_{i}\le v_{j}\) 的 \((i,j)\) 的 \(b_{i}+b_{j}\) 最小值,在两者中再取一个最小值即为答案.
考虑对 \(u,v\) 建一颗权值线段树,考虑到,当 \(i\) 在左子树,\(j\) 在右子树时,一定有 \(u_{i}\le v_{j}\),因此 \((i,j)\) 可以用于更新父亲节点 \(b_{i}+b_{j}\) 的最小值,反之同理,而我们需要的整体最小值,要么就是从子节点继承上来的答案,要么就是该节点的 \(i\) 在左子树,\(j\) 在右子树时的情况或者 \(j\) 在左子树,\(i\) 在右子树时贡献的情况,可以发现这样做正好覆盖了全部合法的 \((i,j)\)
因为在 \((i,j)\) 中,一定有前者为法杖,后者为咒语。因此在增加操作中,遇到法杖 \(i\) 则在 \(a-b\) 位置插入节点 \((a_{i},b_{i})\),遇到咒语 \(j\) 则在 \(b-a\) 位置插入节点 \((a_{j},b_{j})\),然后按顺序向上更新每层法杖的 \(a,b\) 最小值,咒语的 \(a,b\) 最小值,然后在当前节点用左子树法杖,右子树咒语,或者左子树咒语,右子树法杖的最小值分别计算贡献并取最小值即可
关于删除操作,注意到同一个叶节点下方可能会有多个 \((a,b)\),因此考虑建树套树,简化一点开个 set 维护,因为我们需要分别统计四个最值,因此对每个叶节点开四个 set,分别以不同关键字排序,统计答案的时候取首位维护最小值,删除的时候直接暴力搜索删除,记得删除之后要将该节点统计信息清空后重新统计一遍,防止原先较大的答案未被覆盖导致的错误.
处于空间考虑,我们只需要对每个叶节点开一个 set,因此考虑将 set 建在外面,线段树里面只存储 set 的编号来处理. 由于 \(a-b\) 可能存在负数,因此需要整体偏移,鉴于 \(a,b\in[0,2.5e5]\),偏移量为 \(0-2.5e5=-2.5e5\),线段树范围为 \(2.5e5-0-(-2.5e5)=5e5\),最大值不超过 \(2.5e5+2.5e5=5e5\),不用开 long long
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
template<typename T>
void read(T& x){
x=0;bool sym=0;char c=getchar();
while(!isdigit(c)){sym^=(c=='-');c=getchar();}
while(isdigit(c)){x=x*10+c-48;c=getchar();}
if(sym)x=-x;
}
template<typename T,typename... Args>
void read(T& x,Args&... args){
read(x);read(args...);
}
#define tests int cases;read(cases);while(cases--)
#define pb push_back
int Q,T;
const int N=5e5+10,dx=N/2;
const int inf=1e8;
int lastans=0;
struct node{
int a,b;
bool operator <(const node &A)const{
if(a==A.a) return b<A.b;
return a<A.a;
}
};
struct node2{
int a,b;
bool operator <(const node2 &A)const{
if(b==A.b) return a<A.a;
return b<A.b;
}
};
struct setpair{
set<node> pa,qa;
set<node2> pb,qb;
};
vector<setpair>store;
namespace Stree{
#define tol (id*2)
#define tor (id*2+1)
#define mid(l,r) mid=(l+r)/2
struct tree{
int l,r;
int store_id;
int minap,minaq,minbp,minbq;
int minansa,minansb;
}t[4*N];
void pushup(int id){
t[id].minap=inf;
t[id].minaq=inf;
t[id].minbp=inf;
t[id].minbq=inf;
t[id].minansa=t[id].minansb=inf;
if(t[tol].l){
t[id].minap=min(t[tol].minap,t[id].minap);
t[id].minaq=min(t[tol].minaq,t[id].minaq);
t[id].minbp=min(t[tol].minbp,t[id].minbp);
t[id].minbq=min(t[tol].minbq,t[id].minbq);
t[id].minansa=min(t[tol].minansa,t[id].minansa);
t[id].minansb=min(t[tol].minansb,t[id].minansb);
}
if(t[tor].l){
t[id].minap=min(t[tor].minap,t[id].minap);
t[id].minaq=min(t[tor].minaq,t[id].minaq);
t[id].minbp=min(t[tor].minbp,t[id].minbp);
t[id].minbq=min(t[tor].minbq,t[id].minbq);
t[id].minansa=min(t[tor].minansa,t[id].minansa);
t[id].minansb=min(t[tor].minansb,t[id].minansb);
}
if(t[tol].l and t[tor].l){
t[id].minansa=min({t[tol].minansa,t[tor].minansa,t[tol].minaq+t[tor].minap});
t[id].minansb=min({t[tol].minansb,t[tor].minansb,t[tol].minbp+t[tor].minbq});
}
}
void build(int id,int l,int r){
t[id].l=l;t[id].r=r;
t[id].minap=inf,t[id].minaq=inf;
t[id].minbp=inf,t[id].minbq=inf;
t[id].minansa=inf;t[id].minansb=inf;
if(l==r){
store.push_back({});
t[id].store_id=(int)store.size()-1;
return;
}
int mid(l,r);
build(tol,l,mid);
build(tor,mid+1,r);
}
void update(int id,bool isp){
if(!store[t[id].store_id].pa.empty()) t[id].minap=store[t[id].store_id].pa.begin()->a;
else t[id].minap=inf;
if(!store[t[id].store_id].pb.empty()) t[id].minbp=store[t[id].store_id].pb.begin()->b;
else t[id].minbp=inf;
if(!store[t[id].store_id].qa.empty()) t[id].minaq=store[t[id].store_id].qa.begin()->a;
else t[id].minaq=inf;
if(!store[t[id].store_id].qb.empty()) t[id].minbq=store[t[id].store_id].qb.begin()->b;
else t[id].minbq=inf;
t[id].minansa=min(inf,t[id].minap+t[id].minaq);
t[id].minansb=min(inf,t[id].minbp+t[id].minbq);
}
void update2(int id,bool isp,int a,int b){
if(isp){
t[id].minap=min(t[id].minap,a);
t[id].minbp=min(t[id].minbp,b);
}
else{
t[id].minaq=min(t[id].minaq,a);
t[id].minbq=min(t[id].minbq,b);
}
t[id].minansa=min(inf,t[id].minap+t[id].minaq);
t[id].minansb=min(inf,t[id].minbp+t[id].minbq);
}
void change(int id,int pos,bool isp,int a,int b){
if(t[id].l==t[id].r){
if(isp){
store[t[id].store_id].pa.insert({a,b});
store[t[id].store_id].pb.insert({a,b});
}
else{
store[t[id].store_id].qa.insert({a,b});
store[t[id].store_id].qb.insert({a,b});
}
update2(id,isp,a,b);
return;
}
if(pos<=t[tol].r) change(tol,pos,isp,a,b);
else change(tor,pos,isp,a,b);
pushup(id);
}
void remove(int id,int pos,int isp,int a,int b){
if(t[id].l==t[id].r){
if(isp){
auto it1=store[t[id].store_id].pa.lower_bound(node{a,b});
auto it2=store[t[id].store_id].pb.lower_bound(node2{a,b});
if(it1!=store[t[id].store_id].pa.end() and it1->a==a and it1->b==b){
store[t[id].store_id].pa.erase(it1);
store[t[id].store_id].pb.erase(it2);
}
}
else{
auto it1=store[t[id].store_id].qa.lower_bound(node{a,b});
auto it2=store[t[id].store_id].qb.lower_bound(node2{a,b});
if(it1!=store[t[id].store_id].qa.end() and it1->a==a and it1->b==b){
store[t[id].store_id].qa.erase(it1);
store[t[id].store_id].qb.erase(it2);
}
}
update(id,isp);
return;
}
if(pos<=t[tol].r) remove(tol,pos,isp,a,b);
else remove(tor,pos,isp,a,b);
pushup(id);
}
int solve(){
return min(t[1].minansa,t[1].minansb);
}
}
using namespace Stree;
signed main(){
read(Q,T);
build(1,1,N);
for(int i=1;i<=Q;++i){
int op,t,a,b;read(op,t,a,b);
if(T==1){
a^=lastans;b^=lastans;
}
if(op==1){
if(t==0){
change(1,a-b+dx,true,a,b);
}
else{
change(1,b-a+dx,false,a,b);
}
}
else{
if(t==0){
remove(1,a-b+dx,true,a,b);
}
else{
remove(1,b-a+dx,false,a,b);
}
}
int res=solve();
cout<<(lastans=(res==inf?0:res))<<endl;
}
}
[29] CSP模拟2的更多相关文章
- Nescafe #29 NOIP模拟赛
Nescafe #29 NOIP模拟赛 不知道这种题发出来算不算侵权...毕竟有的题在$bz$上是权限题,但是在$vijos$似乎又有原题...如果这算是侵权的话请联系我,我会尽快删除,谢谢~ 今天开 ...
- csp模拟赛低级错误及反思
\(csp\)模拟赛低级错误及反思. 1.没开\(longlong\). 反思:注意数据类型以及数据范围. 2.数组越界(前向星数组未开两倍,一题的数据范围应用到另一题上,要开两倍的写法为开两倍数组) ...
- 2018.06.29 NOIP模拟 Gcd(容斥原理)
Gcd 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出n个正整数,放入数组 a 里. 问有多少组方案,使得我从 n 个数里取出一个子集,这个子集的 gcd 不为 1 ,然后我再从 ...
- 9.27下午考试(Nescafé 29杯模拟赛)
140pts(100+30+10)Rank3 前几天还考了一场,AK,没什么好总结的,所以就没写博客. 炸: T2,模拟退火突然不会了,写个状压dp,排序边的时候sort的N而不是M. 这个坑经常出! ...
- 19.7.29 NOIP模拟10
话说这次三道考试题直接可以连成一个段子:我一个辣鸡,连模板都不会打,只能跪倒在大佬面前; T1 辣鸡 但是我实在是太辣鸡了,最后干的T1,时间不够用,连暴力都没打对,无奈之下交了一个qj程序,60分( ...
- 【CSP模拟赛】Freda的迷宫(桥)
题目描述 Freda是一个迷宫爱好者,她利用业余时间建造了许多迷宫.每个迷宫都是由若干房间和走廊构成的,每条走廊都连接着两个不同的房间,两个房间之间最多只有一条走廊直接相连,走廊都是双向通过. 黄昏 ...
- 【csp模拟赛九】--dfs3
这道题贪心错误:直接dfs就行,枚举新开一个还是往之前的里面塞 贪心代码(80): #include<cstdio> #include<algorithm> #include& ...
- 2019.7.29 NOIP模拟测试10 反思总结【T2补全】
这次意外考得不错…但是并没有太多厉害的地方,因为我只是打满了暴力[还没去推T3] 第一题折腾了一个小时,看了看时间先去写第二题了.第二题尝试了半天还是只写了三十分的暴力,然后看到第三题是期望,本能排斥 ...
- @CSP模拟2019.10.16 - T3@ 垃圾分类
目录 @description@ @solution@ @accepted code@ @details@ @description@ 为了保护环境,p6pou建设了一个垃圾分类器. 垃圾分类器是一个 ...
- CSP模拟赛2游记
这次由于有课迟到30min,了所以只考了70min. 调linux配置调了5min,只剩下65min了. T1:有点像标题统计,但要比他坑一点,而且我就被坑了,写了一个for(int i=1;i< ...
随机推荐
- Vue Axios二次封装
// axios二次封装 import axios from 'axios' import qs from 'qs' /*** *判断环境变量区分接口的默认地址 */ switch (process. ...
- 关于导入react native项目导致运行异常的那些事
从git上导入公司的项目,在本地运行的时候,项目无法运行.sdk,jdk,node都是使用公司规定的版本,项目中的local.properties文件sdk.dir路径也换成了自己本地的目录,结果就在 ...
- .NET跨平台UI框架Avalonia 11.1重磅发布
本篇为译文 原文地址 https://avaloniaui.net/blog/avalonia-11-1-a-quantum-leap-in-cross-platform-ui-development ...
- Linux中&&、&、|、||等特殊符号
&& 和 & & 表示任务后台执行,与nohup命令功能差不多. # 运行jar包,并且置于后台执行,执行的日志重定向到当前默认的log.txt文件中 [root@lo ...
- .NET TCP、UDP、Socket、WebSocket
做.NET应用开发肯定会用到网络通信,而进程间通信是客户端开发使用频率较高的场景. 进程间通信方式主要有命名管道.消息队列.共享内存.Socket通信,个人使用最多的是Sokcet相关. 而Socke ...
- php执行出现权限问题
- Ubuntu 20.04 双系统安装完整教程
1.查看电脑的信息 1.1 查看BIOS模式 "win+r"快捷键进入"运行",输入"msinfo32"回车,出现以下界面,可查看BIOS模 ...
- 【Docker】09 部署挂载本地目录的Redis
1.拉取Redis镜像: docker pull redis:6.0.6 2.执行挂载命令: docker run -d \ --name=redis \ --restart=always \ --p ...
- 【Spring Data JPA】08 多表关系 Part1 一对多关系操作
表关系概述: 1.一 对应 一 一对夫妻,一个男人只能有一个老婆,一个女人只能有一个老公,即这种对应关系 2.一 对应 多 [多对一] 一个年级具有多个班级,一个班级具有对应的所属年级,即这种上下层级 ...
- 国产的AI基础设施与国外的差距?仅以grpc与prpc做比较
搞AI,基础设施包括软件.硬件以及相关生态,多方面,这里只片面的取一个例子来说明国内外在AI基础设施上的区别,注意,这里只是片面截取. 高性能的rpc框架是搞AI的一个基础依赖软件,当然,国外也有与之 ...