『模拟赛』csp-s模拟2

『模拟赛』csp-s模拟1

挂分日寄:104pts

非常好测试数据+重新评测,使我库库掉RK。

T1 不相邻集合

赛时乱糊了个 \(O(n)\) 扩展,大样例过了就没管,最终60pts...只能说太㵘了...

⾸先不相邻集合中不能有两个相同的数,所以每个数只有第⼀次出现是有⽤的。

考虑到一段连续的数 \(i,i+1,i+2,\dots,i+k-1\)\(k\) 个数,此时这一段数的贡献为 \(\lceil \frac{k}{2} \rceil\)

所以只需要维护最长连续子段即可。

可以使用并查集维护,对于一个数 \(x\) ,判断 \(x+1\)\(x-1\) 存不存在,存在就将她(们)和 \(x\) 合并,每次询问求出 \(\lceil \frac{size}{2} \rceil\) 之和即为答案。

但是每次查询都 \(O(n)\) 求一遍和肯定会T,考虑优化:

每次在并查集上合并时直接统计答案,先减去两个分别的答案,再加上总和的答案即可。

int n,m,a[N],ans=0;
int vis[N];

struct DSU{
	int fa[N],siz[N];
	
	void init(){
		for(int i=1;i<=500010;i++){
			fa[i]=i,siz[i]=1;
		}
	}
	
	int find(int x){
		return x==fa[x]?x:fa[x]=find(fa[x]);
	}
	
	void unionn(int x,int y){
		x=find(x),y=find(y);
		if(x==y) return ;
		if(siz[x]<siz[y]) swap(x,y);
		fa[y]=x;
		ans-=ceil((float)siz[x]/2)+ceil((float)siz[y]/2);
		siz[x]+=siz[y];
		
		ans+=ceil((float)siz[x]/2);
	}
}dsu;

signed main(){
	n=rd;
	for(int i=1;i<=n;i++) a[i]=rd;
	
	dsu.init();
	
	for(int i=1;i<=n;i++){
		if(vis[a[i]]){
			printf("%lld ",ans);
			continue;
		}
		vis[a[i]]=1;
		ans++;
		if(vis[a[i]-1]){
			dsu.unionn(a[i]-1,a[i]);
		}
		if(vis[a[i]+1]){
			dsu.unionn(a[i]+1,a[i]);
		}
		printf("%lld ",ans);
	}
	
	return Elaina;
}

T2 线段树

赛时建树+一些小计算亖了...

\(f(n,x)\) 表示根节点序号为 \(x\) ,有 \(n\) 个叶节点的子树的全部贡献

发现其儿子节点的贡献相对于该节点可以表示为该节点的贡献乘以一个系数再加上若干值,即 \(f(n,x)\) 是关于 \(x\) 的一次函数

于是,设

\[f(n,x)=k_nx+b_n \]

\[f(n,x)=f(\lceil \frac{n}{2} \rceil,2x)+f(\lfloor\frac{n}{2} \rfloor,2x+1)+x \]

可知

\[k_nx+b_n=k_{\lceil \frac{n}{2} \rceil}x+b_{\lceil \frac{n}{2} \rceil}+k_{\lfloor\frac{n}{2} \rfloor}x+b_{\lfloor\frac{n}{2} \rfloor}+x \]

解得

\[\begin{cases} k_n=2(k_{\lceil \frac{n}{2} \rceil}+k_{\lfloor\frac{n}{2} \rfloor}) \\ b_n=b_{\lceil \frac{n}{2} \rceil}+b_{\lfloor\frac{n}{2} \rfloor}+k_{\lfloor\frac{n}{2} \rfloor} \end{cases} \]

\(k,b\) 可以记搜得到。

const int mod=1e9+7;
const int N=2e6+100;
const int inf=0x7fffffff;

int n,m,ans;

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)%mod;
}
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))%mod;
}

int work(int rt,int l,int r,int ql,int qr){
	if(l>qr || r<ql) return 0;
	if(ql<=l&&r<=qr){
		return (k(r-l+1)*(rt%mod)%mod+b(r-l+1)%mod)%mod;
	}
	int mid=(l+r)>>1;
	
	return work(rt<<1,l,mid,ql,qr)+work(rt<<1|1,mid+1,r,ql,qr);;
}

signed main(){
	int T=rd;
	while(T--){
		int n=rd,x=rd,y=rd;
		ans=work(1,1,n,x,y)%mod;
		printf("%lld\n",ans);
	}
	return Elaina;
} 

T3 魔法师

赛时考虑用平衡树维护..没时间亖了.改为数组模拟(纯暴力)。

贺题解

对于法杖 \(p\) 和咒语 \(q\),其施展魔法需要的魔力为 \(max(a_p+a_q,b_p+b_q)\)。这种东西一眼看上去就很难维护,所以考虑分讨把她化掉:

具体实现:

得GGrun真传,可以用线段树套multiset,省的建四棵树了。

插入就是直接 insert
删除就先 lower_bound 找一下,找到就 erase
取最小值时因为set内部是升序排列的,所以直接返回 *set.begin() 即可。

有一点要注意插入时因为不知道差值的正负因此要以一个基准为零点。

int n,m;

#define lson (rt<<1)
#define rson (rt<<1|1)

const int B=25e4;

int ans[N],num[N][2][2];
multiset<int> s[500100][2][2];
void add(int rt,int l,int r,int pos,int a,int b,int opt){
	if(l==r){
		if(!s[l][0][0].size())
			s[l][0][0].insert(1e8),
			s[l][1][0].insert(1e8),
			s[l][0][1].insert(1e8),
			s[l][1][1].insert(1e8);
		if(opt<0){
			auto itpos=s[l][-opt-1][0].lower_bound(a);
			s[l][-opt-1][0].erase(itpos);
			itpos=s[l][-opt-1][1].lower_bound(b);
			s[l][-opt-1][1].erase(itpos);
		}else{
			s[l][opt-1][0].insert(a),
			s[l][opt-1][1].insert(b);
		}
		num[rt][0][0]=*s[l][0][0].begin(),//p a
		num[rt][0][1]=*s[l][0][1].begin(),//p b
		num[rt][1][0]=*s[l][1][0].begin(),//q a
		num[rt][1][1]=*s[l][1][1].begin();//q b
		ans[rt]=min(num[rt][0][1]+num[rt][1][1],num[rt][1][0]+num[rt][0][0]);
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) add(lson,l,mid,pos,a,b,opt);
	if(pos>mid) add(rson,mid+1,r,pos,a,b,opt);
	num[rt][0][0]=min(num[lson][0][0],num[rson][0][0]),
	num[rt][0][1]=min(num[lson][0][1],num[rson][0][1]),
	num[rt][1][0]=min(num[lson][1][0],num[rson][1][0]),
	num[rt][1][1]=min(num[lson][1][1],num[rson][1][1]);
	ans[rt]=min({ans[lson],ans[rson],num[lson][0][1]+num[rson][1][1],num[lson][1][0]+num[rson][0][0]});
	return ;
}

signed main(){
	mst(num,0x3f);
	mst(ans,0x3f);
	
	int m=rd,T=rd,lst=0;
	while(m--){
		int opt=rd,t=rd,a=rd,b=rd;
		if(T) a^=lst,b^=lst;
		int k=(t)?b-a:a-b;
		if(opt==1){
			add(1,1,5e5,k+B,a,b,t+1);
			lst=ans[1]>=1000000?0:ans[1];
		}else{
			add(1,1,5e5,k+B,a,b,-t-1);
			lst=ans[1]>=1000000?0:ans[1];
		}
		printf("%lld\n",lst);
	}
	return Elaina;
}

T4 园艺

赛时没时间想DP了,胡乱贪心...呃呃...又亖了...

总结

时间管理大师。

做题策略有问题。

赛后看题解,思路完全不沾边,脑子有问题。

题解有问题。

呃呃,我算是废了。

你在想peach.

posted @ 2024-09-08 10:50  Elaina_0  阅读(18)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end