2021.10.05膜你赛

2021.10.05膜你赛

T1

Description

众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。
小葱同学最近醉心于动态规划的研究,他苦学百年,已经牢牢掌握了加法和减法是怎么运算的。所以小葱同学造了 \(\) 个数 \(_1, _2, ⋯ , _\),他希望对每个数加上或者减去 \(\),使得操作之后的最大值减去最小值的差最小。

Solution

考试的时候想出了正解,结果脑子一歪,错失20分。
假设我们给出的序列为 \(\text{1368},k=3\)
先预处理出 \(+k,-k\) 后的答案。 也就是

-2 0 3 5
4 7 10 11

我们去枚举最大值,也就是第二行,我们的一开始的序列是预先排过序的,假设我们枚举的最大值为 10,那么它往后的肯定不能选择+操作了,但是我们知道即使是减,后面的数也可能会减出一个比我们枚举的更大的,此时真正的最大值为 \(\max(Max_iMin_n,)\) ,因为是让着差最小,我们要让最小值最大,所以当前位置的前面一定都进行加操作,此时的最小值为 \(\min(Max_1,Min_{i+1})\) ,答案就是 \(\min(MAX-MIN)\) 了。

Code

/*
Authour: work by smyslenny
Date: 2021.10.03
Title:
Main idea:
*/

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <queue>
#include <vector>
#include <map>

#define orz cout<<"LKP AK IOI\n"
#define INF 0x3f3f3f3f
#define int long long

using namespace std;
const int M=1e5+5;
int n,k,Ans=INF;
int sz[M];
inline int read()
{
	register int x=0,y=1;
	register char c=getchar();
	while(!isdigit(c)) {if(c=='-') y=0;c=getchar();}
	while(isdigit(c)) {x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
namespace substack1{//爆搜
	
	void get_Ans()
	{
		int Max=-INF,Min=INF;
		for(int i=1;i<=n;i++)
		{
			Max=max(Max,sz[i]);
			Min=min(Min,sz[i]);
		}
		Ans=min(Ans,Max-Min);
	}
	
	void dfs(int pos)
	{
		if(pos>n)
		{
			get_Ans();
			return;
		}
		sz[pos]+=k;
		dfs(pos+1);
		sz[pos]-=k;
		sz[pos]-=k;
		dfs(pos+1);
		sz[pos]+=k;
	}
	
	void main()
	{
		dfs(1);
		printf("%lld\n",Ans);
	}
}
namespace substack2{//k=1
	void main()
	{
		printf("%d\n",sz[n]-sz[1]-2);
	}
}

namespace substack3{//自己想的
	
	int MIN=INF,MAX=-INF,Min[M],Max[M];
	void main()
	{
		
		n=unique(sz+1,sz+1+n)-sz-1;
		for(int i=1;i<=n;i++)
			Min[i]=sz[i]-k,Max[i]=sz[i]+k;
		Ans=sz[n]-sz[1];
		for(int i=2;i<n;i++)
		{
			MAX=max(Max[i],Min[n]);
			MIN=min(Max[1],Min[i+1]);
			Ans=min(Ans,MAX-MIN);
		}
		printf("%lld\n",Ans);
	}
}
namespace substack4{//正解
	
	int Min[M],Max[M];
	void main()
	{
		n=unique(sz+1,sz+1+n)-sz-1;
		Ans=sz[n]-sz[1];
		for(int i=1;i<=n;i++)
			Min[i]=sz[i]-k,Max[i]=sz[i]+k;
//		printf("%d\n",Ans);
		for(int i=1;i<=n;i++)
			Ans=min(Ans,max(Max[i],Min[n])-min(Max[1],Min[i+1]));
		printf("%lld\n",Ans);
	}
}

signed main()
{
	
	n=read(),k=read();
	for(int i=1;i<=n;i++) sz[i]=read();
	sort(sz+1,sz+1+n);
//	if(n<=10) substack1::main();
//	else 
		substack3::main();
	return 0;
}
/*

3 3 
1 3 6
*/

T2

Description

​ 你是能看到第二题的 friends 呢。

​ ——aoao

众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。

小葱同学自幼对语文非常感兴趣,而小葱同学从小也意识到中文与英文最大的一个区别便是分词。也正是因为中文存在分词的问题,才能诞生像“上海市长江大桥”这种脍炙人口的语句。为了将这种魅力带给英文语句,小葱现在把英文语句里面的空格全部删掉了,于是现在小葱想知道,给定一个英文语句和词表,有多少种对其分词的方案呢?

Solution

\(f_i\) 表示字符串前 \(i\) 个字符断句的方案数。

枚举最后一次断句的位置 \(j\),只有\(j+1∼i\) 这段是一个单词,并且 \(f_j\) 这个状态被更新过才能转移。

Code

/*
* @Author: smyslenny
* @Date:    2021.09.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <map>

#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int maxn = 210;
const int mod = 1e9 +7;
const int base = 13131;

int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
int f[maxn],n,len1,ans[maxn],tot;
char s[maxn],ss[maxn];
map<int,bool> mp;
vector<int> edge[maxn];
void dfs(int u, int tot)
{
	ans[tot]=u;
	if(u==len1)
	{
		for(int i=1;i<=len1;i++)
		{
			cout<<s[i];
			if(i!=len1)
				for(int j=1;j<=tot;j++) 
					if(ans[j]==i) 
						printf("_");
		}
		printf("\n");
		return;
	}
	for(int i=0,v;i<edge[u].size();i++)
		v=edge[u][i],dfs(v,tot+1);
}
signed main()
{
	scanf("%s",s+1);
	len1=strlen(s+1);
	n=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ss+1);
		int res=0,len=strlen(ss+1);
		for(int j=1;j<=len;j++) 
			res=(res*base%mod+ss[j]-'a'+1)%mod;
		mp[res]=1;
	}
	f[0]=1;
	for(int i=1;i<=len1;i++)
		for(int j=1;j<=i;j++)
			if(f[j-1])
			{
				int res=0;
				for(int k=j;k<=i;k++) 
					res=(res*base%mod+s[k]-'a'+1)%mod;
				if(!mp[res]) continue;
				f[i]=f[i]+f[j-1];	
				edge[j-1].push_back(i);
			}
	if(f[len1]!=0) 
		dfs(0,1);
	return 0;
}

T3

Description

​ 你是能看到第三题的 friends 呢。

​ ——laekov

众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。

小葱同学今天要出门购物,总共有 \(\) 家商店排成一行,第家商店的物价为 \(_\)

小葱购物从第 \(\) 家商店走到第 \(\) 家商店。在走的过程中,在每家商店小葱可以做下面三件事中的任意一件:

  1. 购入一件商品。

  2. 卖出一件商品,要求这个时候小葱手上至少有一件商品才能卖出。

  3. 路过啥也不干。

现在小葱在行走过程中最多携带一件商品在身上,问假设本金无限的情况下,

小葱从 \(\) 走到 \(\) 的过程中最多赚多少钱

Solution

考试的时候想的贪心是当前点大于下一点,卖出,当前点小于下一点,买入,但是是不对的。

价格是一个波动序列,所以根据贪心策略,要在最高点卖,最低点买,这样差就是最大的
所以用树状数组维护区间和
把一个区间里面那些最高点的和和最低点的相反数的和维护出来

Code

/*
30pts:
暴力dp一下
f[i]表示到i的最大价值
100pts
价格是一个波动序列,所以根据贪心策略,要在最高点卖,最低点买,这样差就是最大的
所以用树状数组维护区间和
把一个区间里面那些最高点的和和最低点的相反数的和维护出来
*/
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	char c=getchar();
	int x=0,f=1;
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*f;
}
int n,m;
namespace PT1
{
	const int N=1010;
	int a[N],f[N];
	int solve(int l,int r)
	{
		memset(f,0,sizeof(f));
		if(l<=r)
		{
			int maxn = -a[l];
			for(int i=l+1; i<=r; ++i)
			{
				maxn=max(maxn,f[i-1]-a[i]);
				f[i]=a[i]+maxn;
			}
			return f[r];
		}
		else
		{
			swap(l,r);
			int maxn=-a[r];
			for(int i=r-1; i>=l; --i)
			{
				maxn=max(maxn,f[i+1]-a[i]);
				f[i]=a[i]+maxn;
			}
			return f[l];
		}
	}
	int main()
	{
		for(int i=1; i<=n; ++i) a[i]=read();
		while(m--)
		{
			int opt=read();
			int l=read(),r=read();
			if(opt==2)
			{
				a[l]=r;
				continue;
			}
			printf("%d\n",solve(l,r));
		}
		return 0;
	}
}
namespace PT2
{
	const int N=100000+100;
	int a[N],tree1[N],tree2[N],bz1[N],bz2[N],ma[N],nn;
	int lowbit(int x)
	{
		return x&-x;
	}
	void update(int *tree,int pos,int c)
	{
		while(pos<=n)
		{
			tree[pos]+=c;
			pos+=lowbit(pos);
		}
	}
	int query(int *tree,int pos)
	{
		int ret=0;
		while(pos)
		{
			ret+=tree[pos];
			pos-=lowbit(pos);
		}
		return ret;
	}
	void updatep_v(int i)
	{
		if(i<=1||i>=n) return ;
		int tmp=query(tree1,i)-query(tree1,i-1);
		update(tree1,i,-tmp);
		tmp=query(tree2,i)-query(tree2,i-1);
		update(tree2,i,-tmp);
		bz1[i]=0;
		bz2[i]=0;
		if(a[i]>a[i-1]&&a[i]>=a[i+1]) bz1[i]=1,update(tree1,i,a[i]);
		if(a[i]<=a[i-1]&&a[i]<a[i+1]) bz1[i]=-1,update(tree1,i,-a[i]);
		if(a[i]>=a[i-1]&&a[i]>a[i+1]) bz2[i]=1,update(tree2,i,a[i]);
		if(a[i]<a[i-1]&&a[i]<=a[i+1]) bz2[i]=-1,update(tree2,i,-a[i]);
	}
	int main()
	{
		for(int i=1; i<=n; ++i) a[i]=read();
		for(int i=2; i<n; ++i)
		{
			if(a[i]>a[i-1]&&a[i]>=a[i+1]) bz1[i]=1,update(tree1,i,a[i]);
			if(a[i]<=a[i-1]&&a[i]<a[i+1]) bz1[i]=-1,update(tree1,i,-a[i]);
			if(a[i]>=a[i-1]&&a[i]>a[i+1]) bz2[i]=1,update(tree2,i,a[i]);
			if(a[i]<a[i-1]&&a[i]<=a[i+1]) bz2[i]=-1,update(tree2,i,-a[i]);
		}
		while(m--)
		{
			int opt=read();
			if(opt==1)
			{
				int l=read(),r=read();
				if(l==r)
				{
					cout<<0<<'\n';
					continue;
				}
				int ans=0;
				if(l<r)
				{
					if(a[l]<a[l+1]) ans-=a[l];
					if(a[r]>a[r-1]) ans+=a[r];
					ans+=query(tree1,r-1)-query(tree1,l);
				}
				else
				{
					swap(l,r);
					if(a[r]<a[r-1]) ans-=a[r];
					if(a[l]>a[l+1]) ans+=a[l];
					ans+=query(tree2,r-1)-query(tree2,l);
				}
				printf("%d\n",ans);
			}
			else
			{
				int p=read(),w=read();
				a[p]=w;
				updatep_v(p-1),updatep_v(p),updatep_v(p+1);
			}
		}
		return 0;
	}
}
int main()
{
	freopen("trade.in","r",stdin);
	freopen("trade.out","w",stdout);
	n=read(),m=read();
	if(n<=1000&&m<=1000)  //pt1--dp
	{
		PT1::main();
	}
	else
	{
		PT2::main();//100pts
	}
	return 0;
}

本欲起身离红尘,奈何影子落人间。
原文地址:https://www.cnblogs.com/jcgf/p/15369876.html