• [省选联考 2022] 序列变换


    一、题目

    点此看题

    二、解法

    话说省选如果能做出这道题就圆满了,但是只拿 \(8\) 分确实应该反思一下。树形结构这东西我甚至专门开了个分类,而且思维技巧中也有总结,这题的转化也比较明显,为什么就是想不到呢?总结的技巧还是多主动去使用吧,这题要是往图论方向想我将绝杀。

    题目转化:如果我们建出括号序列的括号树,那么操作一和操作二的效果等价于,对于某个节点 \(u\) 的任意两个儿子 \(a,b\),我们把 \(b\) 的所有儿子边断开,然后把 \(b\)\(b\) 的儿子都接到 \(a\) 上。

    第一个 \(\tt observation\):我们按照树从上往下,分层操作的顺序最优。这是因为总操作次数是一定的,而你先操作上面会留给下面更多的选择,这是可以让方案代价变得更优的。

    那么此时问题就被简化了许多,开始分类讨论 \(x,y\) 的四种情况:

    • \(x=y=0\),不需要代价。

    • \(x=0,y=1\),那么代价是当前层的权值和\(-\)当前层留下来的权值,显然留下最大值是最优的。

    • \(x=y=1\),考虑权值是 最小值 \(\times(sz-2)\) \(+\) 当前层的权值和( \(sz\) 的含义是考虑到当前层的子树大小,注意不是初始的子树大小),显然留下最大值是最优的。有人说上面的部分是蓝题,可以拿到 \(36\) 分的高分了

    • \(x=1,y=0\),考虑权值是 最小值 \(\times(sz-2)\) \(+\) 当前层留下来的权值,好像就很难贪心了。

    我们从整体的角度来思考权值的含义。可以将权值拆分,初始权值是所有点的权值和,那么我们就要最小化 最小值 \(\times(sz-2)\),直接下传最小值是错误的,因为最后一层要产生负贡献。这里又要以 \(sz\) 分类讨论了:

    • \(sz=1\),不需要操作。
    • \(sz>2\),第二个关键的 \(\tt observation\) 是:我们把最小值和最大值都下传是最优的。因为这样既可以最小化每一层的代价,又可以保证最后一层的负贡献。
    • \(sz=2\),难做。

    但是我们又发现 \(sz=2\) 是一个连续段(先不考虑最后的单点),这个连续段自身是不会产生任何贡献的(因为 \(sz-2=0\)),那么我们可以下放最大值来优化最后一层的负贡献,或者是下放最小值来优化其他层的 最小值 \(\times(sz-2)\),所以我们只需要模拟这两种情况,然后取最小值即可。至于最后 \(sz=2\) 的单点,直接下放最大值就可以了。

    时间复杂度 \(O(n\log n)\),实现基本没有难度。

    三、总结

    主动去发掘问题的图论结构,可以做出第一步转化!

    贪心的关键是观察权值的特点,还可以用拆分法使得贪心的使用更加直观。

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <set>
    using namespace std;
    const int M = 400005;
    #define int long long
    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,x,y,tp,ans,sum,q[M],v[M],sz[M],mx[M],mn[M];
    vector<int> b[M];multiset<int> s;char a[M<<1];
    signed main()
    {
    	n=read();x=read();y=read();scanf("%s",a+1);
    	for(int i=1;i<=n;i++) v[i]=read();
    	for(int i=1,j=0;i<=n<<1;i++)
    	{
    		if(a[i]=='(') q[++tp]=++j;
    		else b[tp].push_back(v[q[tp]]),tp--;
    	}
    	if(x==0 && y==1)
    	{
    		for(int i=1;i<n;i++)
    		{
    			for(int x:b[i]) s.insert(x),sum+=x;
    			sum-=*s.rbegin();ans+=sum;
    			s.erase(--s.end());
    		}
    	}
    	if(x==1 && y==1)
    	{
    		for(int i=1;i<n;i++)
    		{
    			for(int x:b[i]) s.insert(x),sum+=x;
    			ans+=((int)s.size()-2)*(*s.begin())+sum;
    			sum-=*s.rbegin();s.erase(--s.end());
    		}
    	}
    	if(x==1 && y==0)
    	{
    		memset(mn,0x3f,sizeof mn);sz[0]=1;
    		for(int i=1;i<n;i++)
    			sz[i]=b[i].size()+sz[i-1]-1;
    		int u=1,v=1;
    		while(u<n && sz[u]==1) u++;v=u;
    		while(v<n && sz[v]==2) v++;
    		for(int i=u;i<n;i++)
    		{
    			if(i!=v) mn[i]=mn[i-1],mx[i]=mx[i-1];
    			for(int x:b[i]) sum+=x,
    				mn[i]=min(mn[i],x),
    				mx[i]=max(mx[i],x);
    		}
    		int r1=sum-mx[n-1],r2=sum-max(mx[n-1],mx[v-1]);
    		//1 : download the min ; 2 : download the max
    		for(int i=v;i<n;i++)
    			r1+=(sz[i]-2)*min(mn[i],mn[v-1]),
    			r2+=(sz[i]-2)*mn[i];
    		ans=min(r1,r2);
    	}
    	printf("%lld\n",ans);
    }
    
  • 相关阅读:
    RocketMQ4.5.2在centos7的安装
    android 9.x 实现应用内更新安装
    android listview 禁止滚动
    Failed to resolve loader: less-loader
    yarn的 文件名、目录名或卷标语法不正确
    Interceptor无法用Autowired自动注入Bean
    STL文件格式研究
    在C#中用COM操作CAD
    AVEVA CSG 几何图形输出接口
    PDMS数据库快速索引查询
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16187444.html
Copyright © 2020-2023  润新知