8.13模拟:分治&二分&倍增&快速幂

前言

240分
100+80+20+40
T3少取了一个模结果全挂掉了(好不容易推出来了…)
T2也因为各种奇怪的错误挂了分
qwq
吸取教训吧

考场

今天先看题
T1第一眼看错了题意觉得水的不行
T3YBT原题且很水
T2T4乍看不太可做

因为错误审题,决定先写自以为很水的T1
写完样例不对,才发现自己审题完全审错了
那个翻折的关键性质又没有看出来(都看出来了吗,我感觉并不显然啊qwq)
分析了一会性质感觉打表或许可行
但可能是道搬砖题
此时大概8:40
于是转T3稳一稳

此时心态有些不稳了
(我甚至觉得T3的3e7会炸)
把T3超级暴力的《正解》floyd敲完还不太放心
但是也没有什么太好的方法
转回T1死磕
9:30

T1走上了慢慢打表之路
打了30min终于把恶心的转移数组敲完了
此时已经感觉药丸
但是编译结果有点惊喜
一遍过了样例???
又测了几个位置也对了
说实话那个搬砖代码一个bug没有真的挺神奇的
我本来已经做好了和它死磕的准备
结果就这么过了?
重新燃起希望
10:20

来到T2
很不错的时我很快发现了它的关键性质
利用一半为分界点递归转移
然后后面再跟个二分
check的地方递归求个数
很愉快的切掉了这道挺难的题
有一说一这道应该是我这几次模拟做的最好的一道题
确实有逻辑分析在里面
遗憾的是我切掉的时候也有点激动
手一抖最后的一步乘法没有取模

11:00

此时 我以为 我已经切了3题
心态就平和了起来
推了一会T4没有任何思路
就决定写个好一点的部分分下班
20分还是很好写的
40分也不太难
又开始暴力搬砖
11:30

这次时间有些紧凑
T1和T3浪费了太多时间
我没有很多的时间检查了
(说不定再看看T2的沙雕错误就瞅出来了呢qwq)
最后就带着那个bug走出了考场
qwq

复盘

T1 road

正解简单的离谱
关键是回溯时对原图进行一个神奇的反转操作
就很easy了
但为了纪念还是附上打表代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+100;
int n;
int a,b;
int mi[32];
int shunpl[5][5]={{},{0,1,1,1,3},{0,4,2,2,2},{0,3,1,3,3},{0,4,4,2,4}};
int shundir[5][5]={{},{0,2,1,1,2},{0,2,2,1,1},{0,1,2,2,1},{0,1,1,2,2}};
int shunid[5][5]={{},{0,1,2,3,4},{0,4,1,2,3},{0,3,4,1,2},{0,2,3,4,1}};
int nipl[5][5]={{},{0,1,3,1,1},{0,2,2,4,2},{0,3,3,3,1},{0,2,4,4,4}}; 
int nidir[5][5]={{},{0,1,1,2,2},{0,2,1,1,2},{0,2,2,1,1},{0,1,2,2,1}};
int niid[5][5]={{},{0,1,4,3,2},{0,2,1,4,3},{0,3,2,1,4},{0,4,3,2,1}};
int dx[5]={0,0,0,1,1},dy[5]={0,0,1,1,0};
/*
1:shun
2:ni
*/
int x2,y2,x3,y3;
void find(int k,int pl,int dir,int id,int x,int y){
	int ed=id+mi[2*k]-1;
	if((a<id||a>ed)&&(b<id||b>ed)) return;
	if(k==0){
		if(id==a){
			x2=x;y2=y;
		}
		if(id==b){
			x3=x;y3=y;
		}
		return;
	}
	
	if(dir==1){//shun
		int len=mi[k-1],tot=mi[(k-1)*2];
		for(int i=1;i<=4;i++){
			find(k-1,shunpl[pl][i],shundir[pl][i],id+(shunid[pl][i]-1)*tot,x+dx[i]*len,y+dy[i]*len);
		}
		return;
	}
	else{
		int len=mi[k-1],tot=mi[(k-1)*2];
		for(int i=1;i<=4;i++){
			find(k-1,nipl[pl][i],nidir[pl][i],id+(niid[pl][i]-1)*tot,x+dx[i]*len,y+dy[i]*len);
		}
		return;
	}
}
int main(){
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);
	int t;
	scanf("%d",&t);
	mi[0]=1;
	for(int i=1;i<=30;i++) mi[i]=mi[i-1]<<1;
	while(t--){
		scanf("%d%d%d",&n,&a,&b);
		find(n,1,1,1,1,1);
		//printf("a=(%d,%d) b=(%d,%d)\n",x2,y2,x3,y3);
		printf("%.0lf\n",10.0*sqrt((x3-x2)*(x3-x2)+(y3-y2)*(y3-y2)));
	}
	return 0;
}
/*
3
1 1 2
2 16 1
3 4 33
*/

T2 shop

本题std的二分很神奇
类似于一个二分相对大小的最大值
然后硬限制一个范围
就过了…
觉得还是我的代码更好理解一写qwq(所有人对自己的代码都是这么想的)
不取模,见祖宗

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=105;
const int mod=1e9+7;
int n,k;
int solve(int x){
	if(x==0) return 0;
	return x+solve(x/2);
}
int find(int l,int r){
	if(l>r) return 0;
	if(r==0) return 0;
	return r-l+1 + find((l+1)/2,r/2);
}
ll ksm(ll x,int k){
	ll ans=1,res=x;
	while(k){
		if(k&1) ans=ans*res%mod;
		res=res*res%mod;
		k>>=1;
	}
	return ans;
}
void work(int k,int n){
	int tot=solve(n/2);
	if(k<=tot){
		work(k,n/2);return;
	}
	k-=tot;
//	printf("n=%d tot=%d k=%d\n",n,tot,k);
	int o=k/n,p=k%n;
	if(p==0){
		printf("%lld\n",n*ksm(2,o-1)%mod);
		return;
	}
	int st=n/2+1,ed=n;
	while(st<ed){
		int mid=st+ed>>1;
//		printf("  l=%d r=%d find=%d\n",n/2+1,mid,find(n/2+1,mid));
		if(find(n/2+1,mid)>=p) ed=mid;
		else st=mid+1;
	}
	printf("%lld\n",st*ksm(2,o)%mod);
}
int main(){
//	freopen("shop.in","r",stdin);
//	freopen("shop.out","w",stdout);
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&k,&n);
		work(k,n);
	}
	return 0;
}
/*
5
3 1
3 2
5 3
1000000000 1
987654321 876543210

1
32 10
*/

T3 run

YBT原题且很水
这也挂分就离谱
有一个是没有特判m=1的情况
还有一个是因为int的范围k只枚举到30而不是31!
头疼

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=105;
int n,m;
bool f[33][N][N];
int dis[N][N];
int main(){
//	freopen("run.in","r",stdin);
//	freopen("run.out","w",stdout);
	int t;
	scanf("%d",&t);
	while(t--){
		memset(f,0,sizeof(f));
		memset(dis,0x3f,sizeof(dis));
//		if(n==1){
//			printf("0\n");
//			continue;
//		}
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			f[0][x][y]=1;
		}
		for(int k=1;k<=30;k++){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=n;j++){
					//if(i==j) continue;
					for(int p=1;p<=n;p++){
						//if(p==i||p==j) continue;
						if(f[k-1][i][p]&&f[k-1][p][j]){
							f[k][i][j]=1;
//							printf("  p=%d\n",p);
							break;
						}
					}
//					printf("k=%d i=%d j=%d f=%d\n",k,i,j,f[k][i][j]);
				}
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				//if(i==j) continue;
				for(int k=0;k<=30;k++){
					if(f[k][i][j]){
//						printf("ok:k=%d i=%d j=%d\n",k,i,j);
						dis[i][j]=1;break;
					}
				}
			}
			dis[i][i]=0;
		}
		for(int k=1;k<=n;k++){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=n;j++){
					dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
				}
			}
		}
		printf("%d\n",dis[1][n]);
	}
	return 0;
}
/*
1
5 5
1 4
4 1
1 2
2 3
3 5
*/

T4 stairs

这题考场确实不太可做
考后写起来发现反而不难了
状压求出转移矩阵再快速幂加速即可
一个重要的技巧是把0规定为有挡板
这样不同高度之间可以无缝连接

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=105;
const int mod=1e9+7;
int n,k;
int w[8];
struct matrix{
	int x,y;
	ll a[130][130];
	void clear(){
		for(int i=0;i<=x;i++)
			for(int j=0;j<=y;j++) a[i][j]=0;
	}
};
int mi[8];
void print(matrix o){
	for(int i=0;i<=min(3,o.x);i++){
		for(int j=0;j<=3;j++) printf("%d ",o.a[i][j]);
		printf("\n");
	}
	return;
}
matrix cheng(matrix a,matrix b){
	matrix o;
	//printf("Mul:\n") ;
	//print(a);print(b);
	o.x=a.x;o.y=b.y;o.clear();
	for(int i=0;i<=o.x;i++){
		for(int j=0;j<=o.y;j++){
			
			for(int k=0;k<=a.y;k++){
				o.a[i][j]=(o.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
				//if(a.a[i][k]*b.a[k][j])	printf("  i=%d j=%d k=%d %d*%d a=%d\n",i,j,k,a.a[i][k],b.a[k][j],o.a[i][j]);
			}
		}
	}
	return o;
}
int dp[130][2];//dp[i][op]op:下面的边 
matrix trans[8];
void init(){
	mi[0]=1;
	for(int i=1;i<=7;i++) mi[i]=mi[i-1]<<1;
	for(int k=1;k<=7;k++){
		trans[k].x=trans[k].y=mi[7]-1;
		for(int i=0;i<mi[k];i++){
			for(int j=0;j<mi[k];j++){
				dp[0][1]=1;dp[0][0]=0;
				for(int p=1;p<=k-1;p++){
					if((i|j)&mi[p-1]){
						dp[p][1]=dp[p-1][0]+dp[p-1][1];
						dp[p][0]=dp[p-1][1]+dp[p-1][0];
					}
					else{
						
						dp[p][1]=dp[p-1][0];
						dp[p][0]=dp[p-1][1]+dp[p-1][0];
					}
					dp[p][0]%=mod;
					dp[p][1]%=mod;
				}
				if((i|j)&mi[k-1]) trans[k].a[i][j]=dp[k-1][0]+dp[k-1][1];
				else trans[k].a[i][j]=dp[k-1][0];
				//printf("k=%d i=%d j=%d trans=%d\n",k,i,j,trans[k].a[i][j]);
			}
		}
	}
}
matrix ans;
int main(){
	for(int i=1;i<=7;i++) scanf("%d",&w[i]);
	init();
	ans.x=0;ans.y=mi[7]-1;
	ans.a[0][0]=1;for(int i=1;i<=ans.y;i++) ans.a[0][i]=0;
	for(int k=1;k<=7;k++){
			//printf("k=%d trans:\n",k);
			//print(trans[k]);
		while(w[k]){
			//printf("k=%d\n",w[k]);
			if(w[k]&1){
				ans=cheng(ans,trans[k]);
				//printf("ans:\n");
				//for(int i=0;i<=4;i++) printf("%d ",ans.a[0][i]);
				//printf("\n");
			}
			trans[k]=cheng(trans[k],trans[k]);
			//printf("trans:\n");
			//print(trans[k]);
			w[k]>>=1;
		}
	}
	printf("%lld\n",ans.a[0][0]);
	return 0;
}
/*
5
3 1
3 2
5 3
1000000000 1
987654321 876543210

1
32 10
*/

总结

争取吃一堑长一智吧
以后检查代码注意的东西又多了一个:取模是否彻底!
明天:字符串 加油!awa

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值