1004:
题意:给定你一个为1到n的排列,对于这个序列的每一个不为空的子序列,都会存在一个最大值和最小值,然后每一次计算 最小值/最大值 的结果最后把所有的结果相乘,问你这个结果为多少?
思路:维护最大值和最小值,如果直接枚举k,然后求长度为k的最大值和最小值,单调栈,但是 O ( n 2 ) O(n^2) O(n2)。所以考虑去优化这个想法,对于每一位的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(i−L+1)∗(R−i+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