2018提高组模拟8(T1,T2)

2018提高组模拟8

————————————————————————————————————————————20181002

T1

 聚会

(WOJ4018)

描述

在成都的一条街道上,一共有 NN户人家,每个家庭有 XiXi 个人,他们和谐的生活在 一起,作为全国和谐街道,他们经常会小范围组织活动,每次活动会选择一户作为聚点, 并要求某些家庭参加,为了方便通知,村长每次邀请位置连续的家庭。因为每户人数不 同,每个家庭之间有一定距离,村长希望你计算出每次邀请的家庭的移动代价。第 ii个家 庭移动到家庭 jj的代价是:Xi∗dis(i,j)Xi∗dis(i,j)

dis(i,j)dis(i,j)表示ii 到jj的距离,村长一共安排了 mm 次聚会,每次邀请[Li,Ri][Li,Ri]的家庭参加

输入

第一行两个数表示 n,m

第二行 n-1 个数,第 i 个数表示第 i 个家庭与第 i+1 个家庭的距离 Di

第三行 n 个数,表示每个家庭的人数 Xi

之后 m 行每行三个数 x l r,表示查询要把区间 [l,r]的家庭移动到 x 点的代价和

输出

对于每个询问输出一个数表示答案,对 19260817 取模

样例输入

5 5
2 3 4 5
1 2 3 4 5
1 1 5
3 1 5
2 3 3
3 3 3
1 5 5

样例输出

125
72
9
0
70

提示

对于 30%的数据, n,m≤1000

对于另外 20%的数据,所有家庭间的距离都为 1

对于另外 20%的数据,所有家庭人数都为 1

对于 100%的数据 , n,m≤200000;Xi,Di <=2*10^9

题解

(数学推理,前缀和,较容易看懂)

##!!!一定乘的时候要计算long long!!!

减后可能为负数,要加一个mod再求余     (ans+mod)%mod;   !!!!!

#include<bits/stdc++.h>
using namespace std;
int read(){
	int x=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
#define ll long long
const int mod=19260817;
int n,m;
ll s[200010],s1[200010],s2[200010];
int main(){
	n=read();m=read();
	for(int i=1;i<n;i++)
		s[i+1]=(s[i]+read())%mod;
	for(int i=1;i<=n;i++){
		s1[i]=(s1[i-1]+read())%mod;
		s2[i]=(s2[i-1]+(s1[i]-s1[i-1]+mod)%mod*s[i]%mod)%mod;
	}
	int c,l,r;ll e,f;
	while(m--){
		c=read();l=read();r=read();
		e=s[c]*(s1[r]-s1[l-1]+mod)%mod-(s2[r]-s2[l-1]+mod)%mod;
		if(l<=c&&r<=c)printf("%lld\n",(e+mod)%mod);//区间在点左边 
		else if(l>=c&&r>=c)printf("%lld\n",(-e+mod)%mod);//区间在点右边 
		else{//区间在点两侧 
			e=s[c]*(s1[c]-s1[l-1]+mod)%mod-(s2[c]-s2[l-1]+mod)%mod;
			f=(s2[r]-s2[c]+mod)%mod-s[c]*(s1[r]-s1[c]+mod)%mod;
			printf("%lld\n",(e+f+2*mod)%mod);// 
		}
	}
	return 0;
}

 

T2

矩阵分组

 

描述

有N行M列的矩阵,每个格子中有一个数字,现在需要你将格子的数字分为A,B两部分

要求:

1、每个数字恰好属于两部分的其中一个部分

2、每个部分内部方块之间,可以上下左右相互到达,且每个内部方块之间可以相互到达,且最多拐一次弯

如:

AAAAA  AAAAA  AAAAA
AABAA  BaAAA  AAABB
ABBBA  BBAAA  AAABB
AABAA  BaAAA  ABBBB
AAAAA  AAAAA  BBBBB

  (1)     (2)     (3) 

其中(1)(2)是不允许的分法,(3)是允许的分法。在(2)中,a属于A区域,这两个a元素之间互相到达,但是不满足只拐一次弯到达。

问:对于所有合法的分组中,A区域和B区域的极差,其中极差较大的一个区域最小值是多少

提示:极差就是区域内最大值减去最小值。

输入

第一行两个正整数n,m

接下来n 行,每行m个自然数A_{i,j}表示权值

输出

输出一行表示答案

样例输入

4 4
1 12 6 11
11 4 2 14
10 1 9 20
4 17 13 10

样例输出

11

提示

【样例解释】 1 12 6 11 11 4 2 14 10 1 9 20 4 17 13 10

分法不唯一,如图是一种合法的分法。左边部分极差12-1=11,右边一块极差20-10=10,所以答案取这两个中较大者11。没有别的分法,可以使答案更小。

测试点N,m范围
1,2n<=10,m<=10
3-4n=1,m<=2000
5-7n<=200,m<=200
8-10n<=2000,m<=2000

所有权值1<=a_ij<=10^9

题解

(二分+奇葩check,  WOJ4019)

最大的最小,一定为二分。

本题关键在于check函数。

容易想到:阶梯状的分法是符合条件的。

A和B 可以互换,我们不妨设最大极值在A中,那么A 这样一个梯状的区域的顶角处于某个角落,

我们不妨设为左上角,然后将矩阵做 3 次旋转就可以考虑到所有情况。

二分极差值mid,

设最大值在A区域,那么我们从第一行开始找到第一个与最大值差值不在 check 范围内的位置p 
在第二行不超过p-1的位置同样找一个这样的边界, 
这样子我们就将矩阵划分为了两个区域: 
我们枚举出来的左边区域一定满足条件,那么我们再判断右区域满不满足【与最小值的差值在条件范围内】 

 

思想难点

为什么最大值一定在A里?
若最大值在B里:
1.最小值在A里,则gmax-最小值一定大于当前枚举的值,否则答案为gmax-gmin。
2.最小值不在A里,则B里有gmax-gmin,一定大于当前枚举的值,否则答案为gmax-gmin。

所以最大值一定在A里。

#include<bits/stdc++.h>
using namespace std;
int read(){
	int x=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
#define ll long long
const int inf=2000000000;
int n,m,a[4][2005][2005];//四个方向的矩阵 
int gmax=-inf,gmin=inf;//总最大值与最小值 
int endi[2005];//记录每一行的最后一位 
//-----------------------------------------------check函数 
bool check__(int u,int d){//不同方向的矩阵编号 
	if(u&1)swap(n,m);//u为奇数-> 1,3也就是90度,n,m要换一下
	endi[0]=m;
	for(int i=1,j;i<=n;i++){//设最大值在A区域
		for(j=1;j<=endi[i-1];j++)
			if(gmax-a[u][i][j]>d)break;
		endi[i]=j-1;
	}
	//A部分已处理好  判断B部分是否合法 
	for(int i=1;i<=n;i++){
		for(int j=endi[i]+1;j<=m;j++)
			if(a[u][i][j]-gmin>d){
				if(u&1)swap(n,m);//换回
				return 0; 
			}
	}
	if(u&1)swap(n,m);//换回
	return 1;//满足条件,返回真 
}
bool check(int d){
	if(check__(0,d))return 1;
	if(check__(1,d))return 1;
	if(check__(2,d))return 1;
	if(check__(3,d))return 1;
	return 0;
} 
int main(){
	//----------------------------------------------初始化 
	n=read();m=read();
	int x=1,x1=1,x2=n,x3=m;//矩阵绕中心顺时针旋转 
	int y=1,y1=n,y2=m,y3=1,t;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			t=a[0][x][y]=a[1][x1][y1]=a[2][x2][y2]=a[3][x3][y3]=read();
			++y;++x1;--y2;--x3;
			if(t>gmax)gmax=t;
			if(t<gmin)gmin=t;
		}
		++x;y=1;
		--y1;x1=1;//
		--x2;y2=m;
		++y3;x3=m;
	} 
	//-----------------------------------------------二分 
	int l=0,r=gmax-gmin;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	printf("%d",l);
	return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值