zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
    本欲起身离红尘,奈何影子落人间。
  • 相关阅读:
    re | [SWPU2019]ReverseMe
    wp | re | 2020“巅峰极客”网络安全技能挑战赛
    re | [CFI-CTF 2018]IntroToPE
    re | [FlareOn1]Bob Doge
    re | [ACTF新生赛2020]SoulLike
    re | [GKCTF2020]Chelly's identity
    ospf配置与ofps选举DR/BDR
    静态路由的配置
    配置三层交换机实现vlan间通信
    hybrid接口
  • 原文地址:https://www.cnblogs.com/jcgf/p/15369876.html
Copyright © 2011-2022 走看看