• [冲刺国赛2022] 草莓蛋糕


    一、题目

    两个多重集 \(A,B\),其中每个元素都有两个属性 \(a,b\),取 \(x\in A,y\in B\),最小化:

    \[\max(a_x+a_y,b_x+b_y) \]

    \(m\) 次修改,每次修改会对 \(A/B\) 进行一次插入\(/\)删除,你需要动态地维护这个最小值。

    \(m\leq 10^6\)

    二、解法

    考试的时候只会线段树分治,脑子还是不行啊...

    考虑用分类讨论的方法来处理 \(\max\),如果 \(a_x+a_y\geq b_x+b_y\),即 \(a_x-b_x\geq b_y-a_y\),那么最后的取值是 \(a_x+a_y\),设 \(h_x=a_x-b_x,h_y=b_y-a_y\),那么条件就是 \(h_x\geq h_y\);同理如果取值是 \(b_x+b_y\),条件是 \(h_x\leq h_y\)

    那么以 \(h\) 建立一棵线段树,线段树的底层用 \(\tt multiset\) 维护插入和删除,合并的时候由于天然带有 \(h\) 的偏序关系,所以只需要维护四类值的最小值,就可以支持合并,时间复杂度 \(O(n\log n)\)

    三、总结

    本题中的 \(\max\) 相当于把 \(x,y\) 混合到了一起,不好处理;若是使用拆分法,拆分成独立的性质就是便于维护的。

    #pragma GCC optimize("Ofast")
    #pragma GCC target("avx", "sse")
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <set>
    using namespace std;
    const int M = 1000005;
    #define int long long
    const int inf = 2e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,h[M],o[M],op[M],d[M],a[M],b[M];
    int mi[M<<2][4],tr[M<<2];multiset<int> s[M][4];
    void build(int i,int l,int r)
    {
    	tr[i]=2147483647;
    	for(int j=0;j<4;j++) mi[i][j]=inf;
    	if(l==r)
    	{
    		for(int j=0;j<4;j++) s[l][j].insert(inf);
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    }
    void ins(int i,int l,int r,int id,int x)
    {
    	if(l==r)
    	{
    		int p=d[x]==0?0:2;
    		if(op[x]==1)
    		{
    			s[l][p].insert(a[x]);
    			s[l][p+1].insert(b[x]);
    		}
    		else
    		{
    			s[l][p].erase(s[l][p].find(a[x]));
    			s[l][p+1].erase(s[l][p+1].find(b[x]));
    		}
    		tr[i]=min(*s[l][0].begin()+*s[l][2].begin(),
    		*s[l][1].begin()+*s[l][3].begin());
    		for(int j=0;j<4;j++) mi[i][j]=*s[l][j].begin();
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(mid>=id) ins(i<<1,l,mid,id,x);
    	else ins(i<<1|1,mid+1,r,id,x);
    	for(int j=0;j<4;j++)
    		mi[i][j]=min(mi[i<<1][j],mi[i<<1|1][j]);
    	tr[i]=min(min(tr[i<<1],tr[i<<1|1]),
    	min(mi[i<<1][2]+mi[i<<1|1][0],
    	mi[i<<1][1]+mi[i<<1|1][3]));
    }
    signed main()
    {
    	freopen("cake.in","r",stdin);
    	freopen("cake.out","w",stdout);
    	m=read();
    	for(int i=1;i<=m;i++)
    	{
    		op[i]=read();d[i]=read();
    		a[i]=read();b[i]=read();
    		o[i]=h[i]=!d[i]?a[i]-b[i]:b[i]-a[i];
    	}
    	sort(o+1,o+1+m);
    	n=unique(o+1,o+1+m)-o-1;
    	build(1,1,n);
    	for(int i=1;i<=m;i++)
    	{
    		h[i]=lower_bound(o+1,o+1+n,h[i])-o;
    		ins(1,1,n,h[i],i);
    		printf("%lld\n",tr[1]>=inf?-1:tr[1]);
    	}
    }
    
  • 相关阅读:
    配置JDK
    360首页(练习)
    表单练习——(简单的注册页面)
    主页
    证明某字母是否最后一个字母
    方法的重载与重写区别
    什么是设计模式
    java 静态方法和实例方法的区别
    什么是静态方法
    手机充电(练习)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16491771.html
Copyright © 2020-2023  润新知