C. Report
注意到每次排序的区间都是某个前缀,如果先排序了短前缀,再排序长前缀,前面的那次排序可以不用做。所以单调栈维护递减序列,排序一次最长前缀,然后逐个视情况(升序还是降序)输出。
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
#define ll long long
int a[200010];
int ops[200010];
int sta[200010];
int k=0;
void print(int id,int l,int r){
int len=sta[id];
if(id+1<k){
len-=sta[id+1];
if(ops[id] == ops[0]){
print(id+1,l,r-len);
}else{
print(id+1,l+len,r);
}
}
if(ops[id] == ops[0]){
for(int i=r-len+1;i<=r;i++){
printf("%d ",a[i]);
}
}else{
for(int i=l+len-1;i>=l;i--){
printf("%d ",a[i]);
}
}
}
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++){
int op,r;
scanf("%d%d",&op,&r);
while(k>0 && (sta[k-1]<r)){
k--;
}
ops[k] = op;
sta[k] = r;
k++;
}
sort(a,a+sta[0]);
if(ops[0]==2){
reverse(a,a+sta[0]);
}
print(0,0,sta[0]-1);
for(int i=sta[0];i<n;i++){
printf("%d ",a[i]);
}
return 0;
}
D. Messenger
无聊的题,一看就知道是KMP。通过重复次数*26+原字符,构造出新的“字符”。分类讨论单词长度为1,2和其他情况。
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
#define ll long long
const int maxn = 200010;
ll word[maxn];
ll text[maxn];
int _next[maxn];
ll textl[maxn];
char textc[maxn];
ll wordl[maxn];
char wordc[maxn];
int n,m;
ll ans = 0;
void find_next(){
_next[1]=0;
int k = 0;
for(int i=2;i<=m;i++){
while(k && word[i]!=word[k+1]){
k = _next[k];
}
if(word[i]==word[k+1]){
_next[i] = ++k;
}else{
_next[i]=0;
}
}
}
void match(){
int k=0;
for(int i=1;i<=n;i++){
while(k && text[i]!=word[k+1]){
k = _next[k];
}
if(text[i]==word[k+1]){
k++;
}
if(k==m){
if(wordc[0]==textc[i-m] && wordl[0]<=textl[i-m]
&& wordc[m+1]==textc[i+1] && wordl[m+1]<=textl[i+1]){
ans++;
}
k = _next[k];
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%I64d-%c",&textl[i],&textc[i]);
if(textc[i]==textc[i-1]){
textl[i-1]+=textl[i];
n--;
i--;
}
text[i] = textl[i]*26LL + textc[i];
}
for(int i=0;i<m;i++){
scanf("%I64d-%c",&wordl[i],&wordc[i]);
if(wordc[i]==wordc[i-1]){
wordl[i-1]+=wordl[i];
m--;
i--;
}
word[i] = wordl[i]*26LL + wordc[i];
}
if(m==1){
for(int i=1;i<=n;i++){
if(textc[i]==wordc[0] && textl[i]>=wordl[0]){
ans+=(textl[i]-wordl[0]+1);
}
}
}else if(m==2){
for(int i=1;i<n;i++){
if(textc[i]==wordc[0] && textl[i]>=wordl[0]){
if(textc[i+1]==wordc[1] && textl[i+1]>=wordl[1]){
ans++;
}
}
}
}else{
m-=2;
find_next();
match();
}
cout<<ans<<endl;
return 0;
}
E. Product Sum
最优解有两种可能,某个字符往右换和往左换,原理是一样的。
不妨考虑往右换的情况,假如我把第
i
个数换到第
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
#define ll long long
int n;
int a[200010];
ll sum[200010];
ll ans = 0;
void solveR(){
map<int,int> mp;
for(int i=n;i>=1;i--){
auto it = mp.lower_bound(a[i]);
int k;
if(it==mp.end()){
k = n;
}else{
k = it->second;
while(a[k]>a[i]){
k--;
}
}
mp[a[i]]=k;
ll tmp = (a[i]+0LL)*(k-i) - (sum[k]-sum[i]);
ans = max(tmp,ans);
}
}
void solveL(){
map<int,int> mp;
for(int i=1;i<=n;i++){
auto it = mp.upper_bound(a[i]);
int k;
if(it==mp.begin()){
k = 1;
}else{
it--;
k = it->second;
while(a[k]<a[i]){
k++;
}
}
mp[a[i]]=k;
ll tmp = -(a[i]+0LL)*(i-k) + (sum[i-1]-sum[k-1]);
ans = max(tmp,ans);
}
}
int main(){
cin>>n;
ll ans0 = 0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ans0+=(a[i]+0LL)*(i);
}
//calc sum
for(int i=1;i<=n;i++){
sum[i] = sum[i-1] + a[i];
}
solveR();
solveL();
cout<<ans+ans0<<endl;
return 0;
}