SDNU ACM-ICPC 2020 Competition For the End of Term(11-29) 1004、1012

1004:
在这里插入图片描述
题意:给定你一个为1到n的排列,对于这个序列的每一个不为空的子序列,都会存在一个最大值和最小值,然后每一次计算 最小值/最大值 的结果最后把所有的结果相乘,问你这个结果为多少?

思路:维护最大值和最小值,如果直接枚举k,然后求长度为k的最大值和最小值,单调栈,但是 O ( n 2 ) O(n^2) On2。所以考虑去优化这个想法,对于每一位的i,我们考虑这个位置上的数作为最大值或者最小值会产生的贡献。

贡献怎么算呢,每一个位置i的数 a i a_i ai单调栈维护这个数作为最小值和最大值的左右区间端点。最大和最小其实是一样的,只不过最大还要求个逆元。假设为最大(最小)的左右端点为L和R,那么贡献就是 a i ( i − L + 1 ) ∗ ( R − i + 1 ) a_i ^ {(i - L + 1) * (R - i + 1)} ai(iL+1)(Ri+1),这样就可以O(n)扫一遍就能求出答案了。

实在想不出更优的复杂度算法时,考虑每个数的贡献这种思想去优化复杂度。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 7;
const ll MOD = 998244353;
ll q_pow(ll a,ll b){
	ll res = 1;
	a %= MOD;
	while(b){
		if(b & 1) res = res * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return res % MOD;
}

ll inv(ll x){ return q_pow(x,MOD-2); }

int a[MAXN],sta[MAXN],top,Lmi[MAXN],Rmi[MAXN],Lma[MAXN],Rma[MAXN];

int main(){
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);

	for(int i = 1;i <= n;i ++){
		while(top && a[i] < a[sta[top]]) top--;
		if(top) Lmi[i] = sta[top] + 1;
		else Lmi[i] = 1;
		sta[++top] = i;
	}
	top = 0;
	for(int i = n;i >= 1;i --){
		while(top && a[i] < a[sta[top]]) top--;
		if(top) Rmi[i] = sta[top] - 1;
		else Rmi[i] = n;
		sta[++top] = i;
	}
	top = 0;

	for(int i = 1;i <= n;i ++){
		while(top && a[i] > a[sta[top]]) top--;
		if(top) Lma[i] = sta[top] + 1;
		else Lma[i] = 1;
		sta[++top] = i;
	}
	top = 0;
	for(int i = n;i >= 1;i --){
		while(top && a[i] > a[sta[top]]) top--;
		if(top) Rma[i] = sta[top] - 1;
		else Rma[i] = n;
		sta[++top] = i;
	}

	// for(int i = 1;i <= n;i ++){
	// 	printf("%d Lmin = %d,Rmin = %d\n",a[i],Lmi[i],Rmi[i]);
	// }
	// for(int i = 1;i <= n;i ++){
	// 	printf("%d Lmax = %d,Rmax = %d\n",a[i],Lma[i],Rma[i]);
	// }

	ll ans = 1;
	for(int i = 1;i <= n;i ++){
		ans = (ans * q_pow(a[i],(ll)(i - Lmi[i] + 1) * (Rmi[i] - i + 1))) % MOD;
	}
	for(int i = 1;i <= n;i ++){
		ans = (ans * inv(q_pow(a[i],(ll)(i - Lma[i] + 1) * (Rma[i] - i + 1)))) % MOD;
	}

	printf("%lld\n",ans);
	return 0;
}

1012:
在这里插入图片描述
题意:给定你一个N x M的迷宫,问你走出去的最短时间是多少,并输出路径,然后存在多条路径的时候输出字典序大的那条。

思路:bfs模板题,求最大的字典序路径,直接在选择方向的时候,也就是for循环遍历的方向时,先走可行的字典序大的方向,最后先到的一定是字典序大的。

emmmm当时一直在想其他玄学的输出字典序最大的方法,nt了

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 107;
char mp[MAXN][MAXN];
int n,m,vis[MAXN][MAXN];

int dir[4][2] = { {0,-1},{1,0},{-1,0},{0,1} };//按字典序的 W > S > N > E

struct node{
	int x,y,time;
};

bool check(int xx,int yy){
	if(xx < 1 || xx > n || yy < 1 || yy > m) return false;
	if(vis[xx][yy] == 1 || mp[xx][yy] == '#') return false;
	return true;
}

node pre[MAXN][MAXN];
int bfs(int sx,int sy){
	queue<node>q;
	node s;
	s.x = sx,s.y = sy,s.time = 0;
	vis[s.x][s.y] = 1;
	q.push(s);
	while(!q.empty()){
		node p = q.front();
		q.pop();
		if(p.x == n && p.y == m) return p.time;
		for(int i = 0;i < 4;i ++){
			node now = p;
			now.x += dir[i][0];
			now.y += dir[i][1];
			if(check(now.x,now.y)){
				now.time++;
				vis[now.x][now.y] = 1;
				pre[now.x][now.y] = p;
				q.push(now);
			}
		}
	}
}

void output(int x,int y){
	if(x == 1 && y == 1) return ;
	else{
		output(pre[x][y].x,pre[x][y].y);
	}
	int tx = x - pre[x][y].x,ty = y - pre[x][y].y;
	// cout<<tx<<"---"<<ty<<endl;
	if(tx == 0 && ty == -1) printf("W");
	if(tx == 1 && ty == 0) printf("S");
	if(tx == -1 && ty == 0) printf("N");
	if(tx == 0 && ty == 1) printf("E");
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i ++) scanf("%s",mp[i]+1);
	int ans = bfs(1,1);
	printf("%d\n",ans);
	output(n,m);printf("\n");
	return 0;
}

打完结训赛,感觉自己就是代码细节方面差的有点多,有思路和方法,实现不对也太伞兵了qwq

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值