2019.08.20【NOIP提高组】模拟 B 组 排序、DP+递推、矩阵乘法+数位DP/类欧

22 篇文章 0 订阅
12 篇文章 0 订阅
本文讲述了程序员ztxz16在旅行中的几个算法问题,包括在矩形景点中按美观度排序游览以最大化时间、在数轴上散步的方案计数以及等差数列中二进制1的个数。这些问题涉及到动态规划、矩阵快速幂等算法,文章通过故事的形式展示了算法的应用。
摘要由CSDN通过智能技术生成

0 旅游(travel)

ztxz16如愿成为码农之后,整天的生活除了写程序还是写程序,十分苦逼。终于有一天,他意识到自己的生活太过平淡,于是决定外出旅游丰富阅历。

ztxz16生活的城市有NM个景点,可以描述成一个NM的矩形,每个景点有一个坐标(x, y) (1 <= x <= N, 1 <= y <= M)以及美观度A[x][y]和观赏所需的时间B[x][y],从一个景点(x1, y1)走到另一个景点(x2, y2)需要时间为它们之间的曼哈顿距离:|x1 - x2| +|y1 - y2|。

为了防止审美疲劳,ztxz16希望观赏的景点的的美观度是严格上升的,由于不想太早回家码代码,ztxz16希望旅游的总时间尽可能长。

对于30%的数据:1<=N<=50 , 1<=M<=50

对于60%的数据:1<=N<=300 , 1<=M<=300

对于100%的数据:1<=N<=1000 , 1<=M<=1000

0<=A[x][y]<=1000000

0<=B[x][y]<=10^9

注意:本题输入数据较大,请注意输入消耗的时间


因为矩阵的坐标对浏览的顺序没有影响而美观度才是有影响,所以用美观度排序,按美观度的顺序随便dp
(然而这样可以过似乎是因为数据水)
(正解好像还要四边形不等式优化?)


我,差点就可以A过,但是我用了堆排,每次都要删删减减那种,就T飞了
然而并不需要堆,因为没有插入操作
在这里插入图片描述

//#pragma GCC optimize(3)
//#pragma GCC optimize(2)
#include <cstdio>
#include <cstring> 
#include <algorithm>

using namespace std;

const int N=1000006;
int n,m,cnt;
long long ans;
unsigned int a[1003][1003];
long long f[N];
struct cv{
	int w,x,y;
	long long f;
}b[N];

inline int read(){
	char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	int x=0;
	while (ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x;
}

inline int abs(int a){
	return (a>0?a:-a);
}

bool comp(cv a,cv b){
	return a.w<b.w;
}

int main(){
	n=read();m=read();
	for (register int i=1;i<=n;i++){
		for (register int j=1;j<=m;j++){
			a[i][j]=read();
		}
	}
	for (register int i=1;i<=n;i++){
		for (register int j=1;j<=m;j++){
			int c=read();
			if (c==0&&a[i][j]==0) continue;
			b[++cnt].w=a[i][j],
			b[cnt].x=i,b[cnt].y=j,b[cnt].f=c;
		}
	}
	sort(b+1,b+1+cnt,comp);
	int lr=1,ls=0;
	for (int i=1;i<=cnt;){
		int k=b[i].w;
		while (k==b[i].w){
			int kk=b[i].f;
			for (register int j=lr;j<=ls;j++)
			b[i].f=(long long)(b[i].f<b[j].f+abs(b[j].x-b[i].x)+abs(b[j].y-b[i].y)+kk?
					b[j].f+abs(b[j].x-b[i].x)+abs(b[j].y-b[i].y)+kk:b[i].f);
			i++;
		}
		lr=ls+1,ls=i-1;
	}
	for (int i=lr;i<=cnt;i++)
	ans=(ans<b[i].f?b[i].f:ans);
	printf("%lld",ans);
}

1 做梦(dream)

ztxz16旅游归来后十分疲倦,很快就进入了梦中。

在梦中ztxz16结婚生子了,他不得不照顾小宝宝。但这实在太无聊了,于是ztxz16会在散步。梦中ztxz16住在一个类似数轴的街上,数轴上的每个整点是一个街区,每个单位时间内ztxz16可以选择向左走一个街区或者向右走一个街区,但如果他离开家超过m个单位时间小宝宝会有危险,因此ztxz16必须在距离上次在家中不超过m个单位时间内回到家中。

n个单位时间后ztxz16会醒来,他希望此时正好在家中。

ztxz16想知道散步过程可能有多少种不同的散步过程。两个散步过程被认为不同,当且仅当存在至少一个单位时刻ztxz16选择的走向不同。

对于30%的数据:2<=n<=100, 2<=m<=100

对于100%的数据:2<=n<=10^9, 2<=m<=100

n和m均为偶数


快乐递推,在线矩乘

来自题解:
用f[i]代表i时刻回到家中的方案有多少种,观察发现转移可以写成矩阵的形式,于是可以矩阵乘法快速幂转移

设f[i][j] 表示 i 时间,走到 j 格有多少种方案
对于初始的m格,暴力递推转移,即
f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − 1 ] [ j + 1 ] f[i][j]=f[i-1][j-1]+f[i-1][j+1] f[i][j]=f[i1][j1]+f[i1][j+1]

Q:欸这个由j+1的转移难道不是有后效性?[菜鸡瞪眼.jpg]
A:傻瓜每个i去更新i+1不就好了
在这里插入图片描述
初始化:
然而对于求出的结果,我们只关心在偶数时间回到原点的方案数
我们就把f[i][0] (i%2=0) 的值放到存答案的数组a[i][j] 里,要乘2
a[i][i-1]=1 :i时间走到i-1格只有一种方案
a[i][i]=1 :i时间走到i格只有一种方案,就是一直走

然后矩阵自乘快速幂,n/2次方

此人散步m时间回到家中可以视为,此人散步到m/2格,所以只用求此人n/2时间走到m/2格的方案

#include <cstdio>
#include <cstring>

using namespace std;

const int mod=1000000007;
int n,m;
long long f[105][105],a[105][105];
long long ans[105][105];

void calc(long long b[105][105]){
	long long d[105][105];
	memset(d,0,sizeof d);
	for (int i=1;i<=m/2;i++)
		for (int j=1;j<=m/2;j++)
			for (int k=1;k<=m/2;k++)
			d[i][j]=(d[i][j]+ans[i][k]*b[k][j])%mod;
	memcpy(ans,d,sizeof d);
}

void dfs(int x){
	if (x==0) return;
	if (x%2==0) dfs(x/2); else dfs(x-1);
	if (x%2==0) calc(ans); else calc(a);
}

int main(){
	scanf("%d%d",&n,&m);
	f[1][1]=1;
	for (int i=1;i<=m-1;i++)
		for (int j=1;j<=m/2;j++){
			f[i+1][j+1]=(f[i+1][j+1]+f[i][j])%mod;
			f[i+1][j-1]=(f[i+1][j-1]+f[i][j])%mod;
		}
	for (int i=2;i<=m/2;i++) a[i][i-1]=1;
	for (int i=1;i<=m/2;i++) a[1][i]=(f[i*2][0]*2)%mod;
	for (int i=1;i<=m/2;i++) ans[i][i]=1;
	dfs(n/2);
	printf("%lld",ans[1][1]);
}

2 数数(count)

ztxz16从小立志成为码农,因此一直对数的二进制表示很感兴趣。今天的数学课上,ztxz16学习了等差数列的相关知识。我们知道,一个等差数列可以用三个数A,B,N表示成如下形式:

B + A, B + 2 * A, B + 3 * A, …, B + N * A

ztxz16想知道对于一个给定的等差数列,把其中每一项用二进制表示后,一共有多少位是1,但他的智商太低无法算出此题,因此寻求你的帮助。

对于30%的数据:

1<=T<=20 , 1<=A<=10000 , 1<=B<=10^16 , 1<=N<=10^3

对于60%的数据:

1<=T<=20 , 1<=A<=10000 , 1<=B<=10^16 , 1<=N<=10^9

对于100%的数据:

1<=T<=20 , 1<=A<=10000 , 1<=B<=10^16 , 1<=N<=10^12


初见时,她说,“我无力自保,无处可去,无人可依。”
多年后,他说,“我教你射箭,你已有力自保;天下为家,你已有处可去;得他相伴,你已有人可依。”
——“有力自保,有处可去,有人可依,愿你一世安乐无忧”

然后女主就和男主在一起了
然后我最爱的男二就死掉了
然后我第二爱的男三就独守天下了
整本书我明明最不喜欢男主好吗呜呜呜

嘤嘤嘤,你们明明有机会的,为什么不?!!
害得我一想起来就伤心QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值