HGOI-国庆七连测-day5

题解

今天又是暴力的一天…天天都是暴力暴力…还能不能好好学oi了orz
我又把暴力打崩了orz我还没有找到暴力的精髓


在这里插入图片描述


第一题——马里奥(Mario)

【题目描述】

  • 给出一个 m ∗ n m*n mn的字符图,#表示浮岛,_表示半空,马里奥可以从相邻的浮岛自由移动,也可以用梯子爬到上下的浮岛上。给出传送门的位置,马里奥的初始位置是在图的左下角。
  • 求出梯子最少要多长。

  • 这个就是裸暴力嘛…就是二分出长度然后bfs判断是否合法。
  • 无奈二分打错(日常)只有60
  • orz

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
void fff(){
	freopen("mario.in","r",stdin);
	freopen("mario.out","w",stdout);
}
const int N=1010;
int n,m,tar_x,tar_y;
bool mp[N][N],vis[N][N];
struct node{
	int x,y;
};
bool bfs(int x){
	memset(vis,false,sizeof(vis));
	int begin_x=n,begin_y=1;
	queue<node> q;
	vis[begin_x][begin_y]=true;
	q.push((node){begin_x,begin_y});
	while(!q.empty()){
		node temp=q.front();q.pop();
		if(temp.x==tar_x&&temp.y==tar_y) return true;
		if(temp.y+1<=m&&mp[temp.x][temp.y+1]&&!vis[temp.x][temp.y+1]){
			q.push((node){temp.x,temp.y+1});
			vis[temp.x][temp.y+1]=true;
		}
		if(temp.y-1>=1&&mp[temp.x][temp.y-1]&&!vis[temp.x][temp.y-1]){
			q.push((node){temp.x,temp.y-1});
			vis[temp.x][temp.y-1]=true;
		}
		for(int i=temp.x+1;i<=min(temp.x+x,n);i++){
			if(mp[i][temp.y]&&!vis[i][temp.y]){
				q.push((node){i,temp.y});
				vis[i][temp.y]=true;
				break;
			}
		}
		for(int i=temp.x-1;i>=max(temp.x-x,1);i--){
			if(mp[i][temp.y]&&!vis[i][temp.y]){
				q.push((node){i,temp.y});
				vis[i][temp.y]=true;
				break;
			}
		}
	}
	return false;
}
char ch[1110];
int main(){
//	fff();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		cin>>ch;
		for(int j=1;j<=m;j++){
			mp[i][j]=(ch[j-1]=='#');
		}
	}
	scanf("%d%d",&tar_x,&tar_y);
	int l=0,r=(n-tar_x),ans;
	while(l<=r){
		int mid=(l+r)>>1;
		if(bfs(mid)){
			ans=mid;
			r=mid-1;
		}
		else {
			ans=mid+1;
			l=mid+1;
		}
	}
	cout<<ans;
}

第二题——祭司(priest)

【题目描述】

  • 给出 n ( n ≤ 200 ) n(n\leq200) n(n200)组数对 ( l i ≤ r i ≤ 200 ) (l_i\leq r_i \leq200) (liri200)范围,要把这些数对范围分成两组,求出两组和之差的最大绝对值最小。

  • 我好想题目描述的有点绕。
  • 就是一组数对表示一组范围,然后你可以从这个范围里面选值,两个集合当中的和的差最大的最小值就是我们要找的答案。
  • 70 70 70%的数据 n ≤ 10 n\leq10 n10,所以暴力就过了
  • 后面三十分…dasxxx大佬的猴排居然能够AC…orz随机算法牛逼
  • 可以看出,两个集合当中的差最大就是一边全取最大值,另一边全取最小值,然后再反过来就好了。就是某一种分法的答案。
  • 那我们记 s u m 1 , s u m 2 sum1,sum2 sum1,sum2分别为上界和下界之和, s u m sum sum记录上下界的和。然后我们就可以看出
    • a n s = m i n ( m a x ( s u m 1 A − s u m 2 B , s u m 1 B − s u m 2 A ) ) ans=min(max(sum1_A-sum2_B,sum1_B-sum_2A)) ans=min(max(sum1Asum2B,sum1Bsum2A))
    •         = m i n ( m a x ( s u m A − s u m 1 A + B , s u m 2 A + B − s u m A ) ) \ \ \ \ \ \ \ =min(max(sum_{A}-sum1_{A+B},sum2_{A+B}-sum_A))        =min(max(sumAsum1A+B,sum2A+BsumA))
  • 发现 s u m 1 A + B , s u m 2 A + B sum1_{A+B},sum2_{A+B} sum1A+B,sum2A+B好像是个定值,那就之和 s u m A sum_A sumA有关
  • 然后枚举 s u m A sum_A sumA所有可能的情况求最小值就可以了。
  • 这个好像想不出正解也分蛮高的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
void fff(){
	freopen("priest.in","r",stdin);
	freopen("priest.out","w",stdout);
}
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
const int INF=80100;
const int N=210;
int n;
struct node{
	int l,r;
	int sum;
}a[N];
int ans=INF;
bool vis[INF];
int sum1,sum2;
int main(){
//	fff();
	sum1=0,sum2=0;
	n=read();
	for(int i=1;i<=n;i++){
		a[i].l=read(),a[i].r=read();
		a[i].sum=a[i].l+a[i].r;
		sum1+=a[i].l,sum2+=a[i].r;
	}
	vis[0]=true;
	for(int i=1;i<=n;i++){
		for(int j=INF;j>=a[i].sum;j--){
			if(vis[j-a[i].sum]) vis[j]=true;
		}
	}
	for(int i=0;i<=INF;i++){
		if(vis[i]) ans=min(ans,max(abs(sum1-i),abs(sum2-i)));
	}
	cout<<ans;
}

第三题——AK(AK)

【题目描述】

  • 给出n个元素与m次查询。
  • 每次查询区间 [ l i , r i ] [l_i,r_i] [li,ri]的和对2305843008676823040取模,并且对每一个元素进行平方。

  • 这个暴力好像有60分的但我分块打炸了…
  • 我还是没有掌握暴力的精髓orz
  • 看这个模数不像是以前的1e9+7这么和谐,好像有什么玄机…
  • 1111111111111111111111111111111100000000000000000000000000000
  • 这个是二进制打出来的东西,然后你就发现
    • c = 2 29 ∗ 3 ∗ 5 ∗ 17 ∗ 257 ∗ 65537 c=2^{29}*3*5*17*257*65537 c=229351725765537
    • φ ( c ) = 2 59 \varphi(c)=2^{59} φ(c)=259
    • 这个欧几里得拓展定理
  • 所以一个数在经过最多59次平方之后就可以不用进行操作了。
  • 然后在此之前就暴力分块操作
  • 操作完了就直接略过就好了
  • 这个分块打打好像还是比较顺的(但我还是打炸了orz)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#define LL long long
using namespace std;
void fff(){
	freopen("ak.in","r",stdin);
	freopen("ak.out","w",stdout);
}
const int N=70000;
const LL MOD=2305843008676823040;
const int LIMIT=66;
inline LL read(){
	LL x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
LL n,m;
LL a[N];
inline LL mul(LL x,LL y){
	LL ss=0;
	while(y){
		if(y&1) ss=(ss+x)%MOD;
		y>>=1;
		x=(x+x)%MOD;
	}
	return ss;
}
LL block_size;
int belong_block[N];
int vis[N],lim[600];
LL ss[600];
inline LL query(LL l,LL r){
	int L=belong_block[l],R=belong_block[r];
	LL sum=0;
	for(int i=l;i<=min(L*block_size,r);i++) sum=(sum+a[i])%MOD;
									//就是这个地方我没有取min....orz
	if(L!=R)
		for(int i=(R-1)*block_size+1;i<=r;i++) sum=(sum+a[i])%MOD;
	for(int i=L+1;i<=R-1;i++) sum=(sum+ss[i])%MOD;
	return sum;
}
inline void up_data(LL l,LL r){
	int L=belong_block[l],R=belong_block[r];
	if(lim[L]<LIMIT){
		for(int i=l;i<=min(L*block_size,r);i++){
			vis[i]++;
			ss[L]=(ss[L]-a[i]+MOD)%MOD;
			a[i]=mul(a[i],a[i]);
			ss[L]=(ss[L]+a[i])%MOD;
		}
		lim[L]=999;
		for(int i=(L-1)*block_size+1;i<=min(L*block_size,n);i++)
			lim[L]=min(lim[L],vis[i]);
	}
	if(L!=R){
		if(lim[R]<LIMIT){
			for(int i=(R-1)*block_size+1;i<=r;i++){
				vis[i]++;
				ss[R]=(ss[R]-a[i]+MOD)%MOD;
				a[i]=mul(a[i],a[i]);
				ss[R]=(ss[R]+a[i])%MOD;
			}
			lim[R]=999;
			for(int i=(R-1)*block_size+1;i<=min(R*block_size,n);i++)
				lim[R]=min(lim[R],vis[i]);
		}
	}
	for(int i=L+1;i<R;i++)
		if(lim[i]<LIMIT){
			ss[i]=0;
			lim[i]=999;
			for(int j=(i-1)*block_size+1;j<=i*block_size;j++){
				vis[j]++;
				lim[i]=min(lim[i],vis[j]);
				a[j]=mul(a[j],a[j]);
				ss[i]=(ss[i]+a[j])%MOD;
			}
		}
}
int main(){
//	fff();
	n=read(),m=read();
	block_size=sqrt(n);
	for(LL i=1;i<=n;i++){
		a[i]=read();
		belong_block[i]=((i-1)/block_size)+1;
		ss[belong_block[i]]=(ss[belong_block[i]]+a[i])%MOD;
	}
	while(m--){
		LL l,r;
		l=read(),r=read();
		printf("%lld\n",query(l,r));
		up_data(l,r);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值