A. Yet Another Promotion
题意:买土豆,一种卖a元一公斤,买m公斤送一公斤;一种卖b元一公斤。求买n公斤土豆最少花多少钱。
解:完全没有思考,把a*n, b*n, 买尽可能多m倍数的土豆剩下的都买a和都买b,四种情况堆上去取min。
B. Fedya and Array
题意:令一序列相邻两个元素之差为1(一头一尾也算相邻),如果一个数大于它左右两边,称其为局部最大;小于两边则为最小。现给出所有局部最大之和为x,局部最小之和为y,求任意满足要求的最短序列。
解:画一画大概是一个折线图,观察一下发现所有局部最大之和与局部最大个数没啥关系,再观察一下样例,序列长度为最大最小值相差距离的两倍。那么构造一个序列,其最小值为y,最大值为x,然后两边连起来即可。例如:x=2, y=-1,序列可以是-1, 0, 1, 2, 1, 0.
C. Dora and Search
题意:给出一个排列,求其一个子数组,使得子数组两端都不是该子数组最大值,也不是该子数组最小值。
解:试图枚举每个元素作为左端点,寻找右端点。预处理出每个元素右起第一个大于/小于它的元素位置,这个过程可以用单调栈解决。假设第一个大于它的位置为n,第一个小于它的位置为m,那么我们只能取位置大于等于max(m, n)的元素,才能使得当前元素不是最值。现在考虑判断右端点是否会为最值,同样地预处理出每个元素向左第一个大于/小于它的数的位置,看当前元素是否在这个范围外。
upd:早上醒来发现昨天时间复杂度是算错的,但应该跑不满n2,有没有好心人算算(
代码:
#include <bits/stdc++.h> using namespace std; #define maxx 200005 #define maxn 25 #define maxm 205 #define ll long long #define inf 1000000009 #define mod 2520 int a[maxx]={0}; int biggerleft[maxx]={0}; int biggerright[maxx]={0}; int smallerleft[maxx]={0}; int smallerright[maxx]={0}; stack<int> s; signed main(){ int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } while (!s.empty()) s.pop(); for(int i=1;i<=n;i++){ while(!s.empty() && a[s.top()] >= a[i]) s.pop(); if(!s.empty()) smallerleft[i]=s.top(); else smallerleft[i]=-1; s.push(i); } while (!s.empty()) s.pop(); for(int i=1;i<=n;i++){ while(!s.empty() && a[s.top()] <= a[i]) s.pop(); if(!s.empty()) biggerleft[i]=s.top(); else biggerleft[i]=-1; s.push(i); } while (!s.empty()) s.pop(); for(int i=n;i>=1;i--){ while(!s.empty() && a[s.top()] >= a[i]) s.pop(); if(!s.empty()) smallerright[i]=s.top(); else smallerright[i]=-1; s.push(i); } while (!s.empty()) s.pop(); for(int i=n;i>=1;i--){ while(!s.empty() && a[s.top()] <= a[i]) s.pop(); if(!s.empty()) biggerright[i]=s.top(); else biggerright[i]=-1; s.push(i); } int flag=0; for(int i=1;i<=n;i++){ if(smallerright[i]==-1||biggerright[i]==-1) continue; int r=max(smallerright[i],biggerright[i]); for(int j=r;j<=n;j++){ if(smallerleft[j]==-1||biggerleft[j]==-1) continue; int l=min(smallerleft[j],biggerleft[j]); if(i<=l){ printf("%d %d\n",i,j); flag=1; break; } } if(flag) break; } if(!flag) printf("-1\n"); } return 0; }
D. Moscow Gorillas
题意:给出两个长度相同的排列,求有多少个区间使得两序列在该区间MEX相同。
解:先观察一下,首先整个排列MEX相同,其次每单个数,除了1,MEX也相同。考虑枚举MEX。如果两个区间MEX相同,那么它们包含1-x-1,同时不包含x。单独考虑1,答案为两序列中都不包含1的区间。对于MEX≥2,预处理两个序列都包含1-x-1的区间,然后根据x的位置向左向右扩展,将扩展部分相乘即可。