• [省选集训2022] 模拟赛4


    A

    题目描述

    给定一个长度为 \(n\) 的数列 \(a\) 和常数 \(c\),将其划分成若干段,设 \(b_i\) 表示第 \(i\) 段的最大值:

    \[\sum_{i=2}^m (b_i-b_{i-1})^2+c \]

    特别地,如果只划分出一段(\(m=1\))则答案为 \(0\)

    \(2\leq n\leq 10^6,1\leq a_i\leq 10^6,0\leq c\leq 10^{10}\)

    解法

    \(dp[i]\) 表示 \(a_i\) 为最后一段的最大值的答案,转移枚举上一段的最大值点 \(j\)

    \[dp[i]=\max\{dp[j]+(a_i-a_j)^2+c\} \]

    转移的条件是 \([j,i]\) 可以被划分成两段使得 \(a_j,a_i\) 分别是这两段的最大值。考虑 \(k\in[i,j]\),如果 \(\exist\ a_k>a_i\and a_k>a_j\),那么肯定是划分不动的,但是考虑此时\(k\) 转移一定比 \(j\),所以不用考虑 \(j\) 合不合法,因为如果 \(j\) 不合法那么一定没用。

    那么直接斜率优化即可,时间复杂度 \(O(n\log n)\),所以你按照 \(a_i\) 单增去打这题就可以过掉。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 1000005;
    #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,m,c,ans;
    struct node
    {
    	int k,b;
    	node(int K=0,int B=-9e18) : k(K) , b(B) {}
    	int ask(int x) {return k*x+b;}
    }t[M<<2];
    void ins(int i,int l,int r,node nw)
    {
    	int mid=(l+r)>>1;
    	if(nw.ask(mid)>t[i].ask(mid)) swap(t[i],nw);
    	if(l==r) return ;
    	if(nw.ask(l)>t[i].ask(l)) ins(i<<1,l,mid,nw);
    	if(nw.ask(r)>t[i].ask(r)) ins(i<<1|1,mid+1,r,nw);
    }
    int ask(int i,int l,int r,int x)
    {
    	int res=t[i].ask(x),mid=(l+r)>>1;
    	if(l==r) return res;
    	if(mid>=x) return max(ask(i<<1,l,mid,x),res);
    	return max(ask(i<<1|1,mid+1,r,x),res);
    }
    signed main()
    {
    	freopen("array.in","r",stdin);
    	freopen("array.out","w",stdout);
    	n=read();c=read();m=1e6;
    	for(int i=1;i<=n;i++)
    	{
    		int x=read();
    		int dp=max(0ll,ask(1,1,m,x)+x*x+c);
    		ans=max(ans,dp);
    		ins(1,1,m,node(-2*x,dp+x*x));
    	}
    	printf("%lld\n",ans);
    }
    

    B

    题目描述

    这是一道交互题,交互库有下列三个可调用的函数:

    int qry1(int x);//返回 a[x]
    vector<int> qry2(vector<int> &s);//返回内含 |a[s[i]]-a[s[j]]| (i>j) 的无序集合
    void answer(vector<int> &ans);//给出交互答案,调用该函数即意味着结束程序
    

    有长度为 \(n\) 的数组 \(a\),满足 \(a_i\) 互不相同,请通过交互求出这个数组。你不需要实现主函数,你只需要实现下列函数,并且要求 \(\tt qry1\) 的调用次数不超过 \(m_1\) 次,\(\tt qry2\) 的调用次数不超过 \(m_2\) 次,\(\tt answer\) 的调用次数恰好为一次。

    void find(int n,int m1,int m2);
    

    对于最大范围的数据满足:\(n\leq 250,m_1=2,m_2=30\)

    解法

    这种确定序列的题目有常规做法,那就是先找关键点,然后再确定整个序列

    有一个 \(\tt observation\):我们从 询问 \(\{s\cup x\}\) 得到的集合 中除掉 询问 \(\{s\}\) 得到的集合,可以得到 \(s\) 中所有数和 \(x\) 的距离,那么我们的关键点可以设置成最大值或者最大值。

    先问一次可以得到序列的极差,那么我们二分一个前缀是否包含极差,就可以得到位置 \(x\)(它是最大值或者最小值,但是暂时不清楚),这部分的花费是 \(1\log n\) 的。

    然后我们确定整个序列,可以二进制分组(或者直接分治,原理都是一致的),因为每个距离都可以唯一代表一个值,我们只需要确定距离对应的位置。那么可以把包含 \(0,1,2...\log n\) 这些二进制位的位置都取出来,然后直接询问这个集合 \(\{s\}\) 和集合 \(\{s\cup x\}\),把得到的距离的 \(pos\) 都加上 \(2^i\)

    那么最后的 \(pos\) 就是这个距离对应着的位置,小细节是这样做必须从 \(1\) 开始编号最好。并且我们可以得到另一个最值的位置,把它们两个都问一下就可以得到 \(x\) 是最大值还是最小值了。这一部分的消耗是 \(2\log n\) 的,所以总共的消耗是 \(3\log n\),足以通过本题。

    #include "difference.h"
    #include <vector>
    #include <algorithm>
    #include <map>
    using namespace std;
    #define pb push_back
    #define vi vector<int>
    void find(int n,int M1,int M2)
    {
    	vi A,B,ans;map<int,int> mp;
    	for(int i=0;i<n;i++) A.pb(i);B=qry2(A);
    	int m=*max_element(B.begin(),B.end());
    	int l=1,r=n-1,x=0,t=0,p[505]={};
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;A.clear();
    		for(int i=0;i<=mid;i++) A.pb(i);
    		B=qry2(A);t=*max_element(B.begin(),B.end());
    		if(t==m) r=mid-1,x=mid;
    		else l=mid+1;
    	}
    	for(int i=0;(1<<i)<=n;i++)
    	{
    		A.clear();
    		for(int j=0;j<n;j++)
    			if(((j+1)&(1<<i)) && j!=x) A.pb(j);
    		vi C=qry2(A);map<int,int> nw;
    		A.pb(x);B=qry2(A);
    		for(auto x:B) nw[x]++;
    		for(auto x:C) nw[x]--;
    		for(auto x:nw) if(x.second)
    			mp[x.first]+=(1<<i);
    	}
    	auto it=mp.end();it--;int u=it->second;
    	p[u]=qry1(u-1);p[x+1]=qry1(x);
    	int fl=(p[u]>=p[x+1])?1:-1;
    	for(auto t:mp) p[t.second]=t.first*fl+p[x+1];
    	for(int i=1;i<=n;i++) ans.pb(p[i]);
    	answer(ans);
    }
    
  • 相关阅读:
    JavaScript中双叹号“!!”作用
    JavaScript两个变量的值交换的多种方式
    自定义npm包——typeScript版本
    自定义npm包的创建、发布、更新和撤销
    vuex概念总结及简单使用实例
    详解javascript: void(0);
    面向对象的CSS
    vue指令概览
    实现水平居中的办法
    C#分块下载文件
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15872166.html
Copyright © 2020-2023  润新知