2018.11.4 noip普及组模拟赛4

1.怪怪の出行

题目大意:输入一段数列长度以及对该数列的操作次数;输入一个字符,对于ABC有不同的操作:A:输出从一到当前位置的人数和,B:第p节车厢加入m个人,C:第p节车厢下去m个人

方法一(暴力):每次输入,A:从1到当前位置暴力枚举,再累加输出;B a[p]+=m;C:a[p]-=m;

不过,方法一对于100%的数据并不能过,所以要思考better的算法

我们可以不暴力枚举,而是用一个t,ans,用来记录怪怪到上一次查询时的位置和答案,当要再一次查询,就把上一次位置之后的人到当前位置的人加在一起;当输入的时B或C时,再判断一下m和t的关系

int t=0,ans=0;
cin>>n>>k;
	for(int i=1;i<=k;i++)
	{
		cin>>ch;
		if(ch=='A'){
			cin>>m;
			for(int j=t+1;j<=m;j++) 
			 ans+=a[j];
			cout<<ans<<endl;
			t=m;
		}
		if(ch=='B'){
			cin>>m>>p;
			if(m<=t)//上车的人在怪怪之前 
			 ans+=p;
			else a[m]+=p;
		}
		if(ch=='C'){
			cin>>m>>p;
			if(m<=t)ans-=p;//下车的人在怪怪之前 
			 else a[m]-=p;
			
		}
	}

2.怪怪の复查

题目大意:一个长度为n的数列a,对a进行m次操作,每次给定L,R,对[L,R]区间进行加值操作,对该区间的每一个值加上1,求出最后a数列中为0的数量

方法一(暴力):按照题意模拟

cin>>n>>m;
for(int i=1;i<=m;i++)
{
  cin>>l>>r;
  for(int j=l;j<=r;j++)a[i]++;
}
for(int i=1;i<=n;i++)
 if(a[i]==0)ans++;
cout<<ans<<endl;

显然,方法一并不能A

方法二:利用前缀和与差分优化   

每次在a[L]加上一个k,a[R+1]减去一个k。最后做一遍前缀和,就得出新的a数列(k=1),最后在枚举一遍,值为0 ans++

#include <bits/stdc++.h>
using namespace std;
int n,m,a,b,ans=0;
int check[200010]={};
inline int read()
{
	int w=1,s=0;
	char ch=getchar();
    while(ch<'0'||ch>'9')if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9'){
    	s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
int main()
{	
	n=read();
	m=read();
	while(m--)
	{
		int a=read(),b=read();
		check[a]++,check[b+1]--;
	}
	for(int i=1;i<=n;i++)
	{
		check[i]+=check[i-1]
		if(!check[i])ans++;
	}
	cout<<ans<<endl;
	return 0;    
}

3.怪怪の加密

题目大意:将一个字符串操作,使得该字符串任意两个相邻的字符不同

方法:栈模拟,遇到和栈顶相同的,则弹出栈顶,否则压入栈。最后从栈底按顺序导出即可

#include<bits/stdc++.h>
using namespace std;
string s;
char a[1000010]={};
int t=1;
int main()
{
//	freopen("lock.in","r",stdin);
//	freopen("lock.out","w",stdout);
	cin>>s;
	for(int i=0;i<s.size();i++)
	{
		 if(a[t-1]==s[i])t=t-1;
		  else a[t]=s[i],t++;
	}
	for(int i=1;;i++)
	 if(a[i]!=0)cout<<a[i];
	  else return 0;
	return 0;
}

4.方方方

方法一:(六重循环)(暴力)

 我们可以六重循环,直接直接暴力枚举三个点的位置,再进一步算出花费,判断是否再l与r之间。(代码不再给出,思路简单明了)

优化:

在此之前,我们要分析题目。对于方格图中任意三个点,花费将是某个矩形的周长,如图

在4*8的方格图中,选(1,1),(3,4),(4,3)三个点,花费=a+b+c,即一个矩形的周长。但是,在该矩形中,其他的点也可以构成该矩形……所以,我们要think

对于i行j列的矩形,寻找三个点,应该有4*(i-2)*(j-2)+2*(i-2)*(j-2)中情况。具体过程如下

{

     

一个矩形,有四条边,这四条边上每条上都得有一个点,可最多只有三个点,所以至少有一个点在矩形的顶点上。接下来分两种情况:

 ①:

两点在顶点上,第三点在中间的矩阵。题目中说,三点之中任意两点都不在同一行同一列,所以前两点有两种情况,左上右下和左下右上,第三点在中间的小矩阵可以任意取,为(i-2)*(j-2) ∴第一种情况为2*(i-2)*(j-2)

②:  

一点在顶点上,有四种情况;剩余两点在顶点不去的两边上,情况也为(i-2)*(j-2),总共有4*(i-2)*(j-2)

综上所述,对于i行j列的矩形,取三个点,有6*(i-2)*(j-2)种情况

 

再加上n行m列的矩形取i行j列的矩形有(n-i+1)*(n-j+1)个

所以,我们就可以以o(1)的时间得出在n*m的矩阵中,取大小为i*j的方案和

就得出了o(nm)的算法:i,j枚举矩阵大小,判断花费是否在l到r之间,如果可以,就j加上方案和,代码如下

#include<bits/stdc++.h>
using namespace std;
const long long Mo=1000000007;
long long  n,m,l,r,ans=0;
inline int read()
{
	int w=1,s=0;
	char ch=getchar();
    while(ch<'0'||ch>'9')if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9'){
    	s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
int main()
{
	n=read();m=read();l=read();r=read();
    for(int i=3;i<=n;i++)
     for(int j=3;j<=m;j++)
     {
     	if(i-1+j-1+i-1+j-1>=l&&i-1+j-1+i-1+j-1<=r)ans+=(4*(i-2)*(j-2)+2*(i-2)*(j-2))%Mo*(n-i+1)%Mo*(m-j+1)%Mo;
	 }
	cout<<ans%Mo<<endl;
	return 0; 
}

至此,我们就可以A了这题啦,代码非常短,但过程特别艰辛,尤其是考试的时候……

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值