其他分享
首页 > 其他分享> > 7.22模拟赛

7.22模拟赛

作者:互联网

目录

7.22模拟赛

甲国的军队 \((army)\)

简单的贪心,手玩一下就能得出结论

假设现在攻打两个城市 \(1\),\(2\)

先打 \(1\) 的代价为 \(b_1+b_2-(b_1-a_1)=a_1+b_2\)

先打 \(2\) 的代价为 \(a_2+b_1\)

按 \(1\) 小于 \(2\) 排序就行

code

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

struct war{
	int a,b;
	inline bool operator <(const war c)const {
		return a+c.b<b+c.a;
	}
}a[N];

signed main(){
	int T=read();
	while(T--){
		int n=read();
		for(int i=1;i<=n;++i){
			a[i].a=read(),a[i].b=read();
			a[i].b=max(a[i].a,a[i].b);
		}
		sort(a+1,a+n+1);
		long long now=0,ans=0;
		for(int i=1;i<=n;++i){
			if(now<a[i].b){
				ans+=a[i].b-now;
				now=a[i].b-a[i].a;
			}
			else
				now-=a[i].a;
		}
		printf("%lld\n",ans);
	}
}

虚弱 \((weakness)\)

这个东西带有绝对值,画成有关 \(x\) 的图像显然是中间小两边大

显然是下凸函数,可以三分

每次 \(O(n)\) 跑一遍

注意精度

code

#include<bits/stdc++.h>
using namespace std;

#define eps 1e-11

const int N=2e5+5;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n;
double a[N];
double b[N],s[N],f[N],g[N];
double sum,ans;

inline double check(double x){
	for(int i=1;i<=n;++i) b[i]=(double)a[i]-x;
	for(int i=1;i<=n;++i) s[i]=s[i-1]+b[i];
	f[1]=g[1]=s[1];
	double mx=-1e18,mn=1e18;
	for(int i=2;i<=n;++i) f[i]=max(f[i-1],s[i]),g[i]=min(g[i-1],s[i]);
	//for(int i=1;i<=n;++i) printf("%lf %lf %lf %lf\n",b[i],s[i],f[i],g[i]);
	for(int i=1;i<=n;++i) mx=max(mx,s[i]-g[i-1]),mn=min(mn,s[i]-f[i-1]);
	return max(fabs(mx),fabs(mn));
}

signed main(){
	n=read();
	for(int i=1;i<=n;++i){
		scanf("%lf",&a[i]);
		s[i]=s[i-1]+a[i];
	}
	double l=-1e4,r=1e4;
	while(r-l>eps){
		double mid=(r-l)/3;
		double nowl=l+mid,nowr=r-mid;
		//printf("%lf %lf %lf %lf %lf\n",l,r,mid,nowl,nowr);
		if(check(nowl)<check(nowr)) r=nowr,ans=nowl;
		else l=nowl,ans=nowr;
	}
	printf("%.6lf\n",check(ans));
}

萨鲁曼的半兽人 \((orc)\)

这题容易想到先跑 \(manacher\),然后搞一个 \(dp\)

我们设 \(pre_i\) 表示 \(i\) 满足 \(j\sim i\) 是回文串的左侧最远的位置 \(j\) ,\(f_i\) 表示连好前 \(i\) 个字符所需的最小次数

转移方程式 \(f_i=\min_{pre_i-1}^{i-1}\{f_j\}+1\),时间复杂度 \(O(n^2)\)

考虑这个过程可以用线段树维护,将时间优化到 \(O(n\log n)\)

考场上调了 \(2h\) 最后发现 \(manacher\) 写炸了,\(zbl\)

考场后发现解法很多,比如堆优化贪心,单调栈优化 \(dp\),甚至还有随机化,而且最劣是 \(n^2\) 的暴力也能过?

事实证明,智商不够只能用数据结构 \(QAQ\),代码相对来说巨长

\(\%\%\%\) 退火教主

code

#include<bits/stdc++.h>
using namespace std;

const int N=2e5+5;
const int inf=2e9;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

namespace ST{
	struct node{
		int mn;
		int l,r;
	}t[N<<2];
	#define lsp p<<1
	#define rsp p<<1|1
	inline void push_up(int p){
		t[p].mn=min(t[lsp].mn,t[rsp].mn);
	}
	inline void build(int p,int l,int r){
		t[p]={inf,l,r};
		if(l==r) return;
		int mid=l+r>>1;
		build(lsp,l,mid);
		build(rsp,mid+1,r);
	}
	inline void update(int p,int x,int k){
		if(t[p].l==t[p].r){
			t[p].mn=k;
			return;
		}
		int mid=t[p].l+t[p].r>>1;
		if(x<=mid) update(lsp,x,k);
		else update(rsp,x,k);
		push_up(p);
	}
	inline int query(int p,int l,int r){
		if(l<=t[p].l&&t[p].r<=r)
			return t[p].mn;
		int mid=t[p].l+t[p].r>>1,res=inf;
		if(l<=mid) res=min(res,query(lsp,l,r));
		if(mid<r) res=min(res,query(rsp,l,r));
		return res;
	}
}

int n,m;
char s[N],t[N];
int p[N],g[N],f[N],pre[N];

inline void pre_work(){
	for(int i=1;i<=n;++i){
		t[i*2-1]='#';
		t[i*2]=s[i];
	}
	t[0]='$',t[n*2+1]='#',t[2*n+2]='&';
	m=2*n+1;
	//for(int i=1;i<=2*n+1;++i) cout<<t[i]<<" ";cout<<endl;
}

inline void manacher(){
	int mr=0,mid=0;
	memset(p,0,sizeof(p));
	for(int i=1;i<=m;++i){
		p[i]=min(p[2*mid-i],p[mr-i]);
		while(i-p[i]&&t[i-p[i]]==t[i+p[i]]) ++p[i];
		if(i+p[i]>mr){
			mr=i+p[i];
			mid=i;
		}
	}
	//for(int i=1;i<=m;++i) cout<<p[i]<<" ";cout<<endl;
}

inline int doge_pride(){
	for(int i=1;i<=m;++i) f[i]=inf;
	f[0]=f[1]=0;
	for(int i=1;i<=m;++i) pre[i]=inf;
	ST::build(1,1,m);
	ST::update(1,1,0);
	for(int i=1;i<=m;++i){
		if(i+p[i]-1<=m) pre[i+p[i]-1]=min(pre[i+p[i]-1],i-p[i]+1);
		if(i!=p[i]) continue;
		if(i+p[i]-1>=m) f[m]=0;
		else f[i+p[i]-1]=0;
	}
	//for(int i=1;i<=m;++i) cout<<f[i]<<" ";cout<<endl;
	for(int i=2;i<=m;++i){
		f[i]=min(f[i],ST::query(1,pre[i],i-1)+1);
		ST::update(1,i,f[i]);
		//cout<<i<<" "<<f[i]<<" "<<i-p[i]<<endl;
	}
	//for(int i=1;i<=m;++i) cout<<f[i]<<" ";cout<<endl;
	return f[m];
}

signed main(){
	while(scanf("%s",s+1)!=EOF){
		n=strlen(s+1);
		//if(n%2==0) continue;
		pre_work();
		manacher();
		printf("%d\n",doge_pride());
	}
}

序列 \((seq)\)

比较容易想到 \(O(n^2)\) 的贪心:

显然这样的做法是正确的,因为这样就相当于先把中间的两两拿出来再拿两边的

这样可以打出 \(O(n^2)\) 的暴力

考虑这个东西可以用线段树维护,我们只需要维护:

一个区间内编号为奇数的最小值和编号为偶数的最小值

区间翻转操作

此题结束

标签:ch,int,mid,7.22,while,isdigit,模拟,getchar
来源: https://www.cnblogs.com/into-qwq/p/16507895.html