2021.10.11 提高组模拟
总的来说考得一般般……100+10+85+10,就那样。
T1 a
题意:
给定一个长度为 \(N(N\le 10^7)\) 的仅包含小写字母的字符串。请你求出其字典序最大的子序列。
解法
题解是什么不重要,反正我的玄学方法能过就行了。我的做法是先把所有的字符z找出来输出,再找y,依次下去,然而这个带常数的做法目前为止还是最优解。
#include<cstdio>
//#define zczc
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
int m,sum[30];
char w;
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);
w=getchar();
while(w<'a'||w>'z')w=getchar();
sum[w-'a']++;
for(int i=2;i<=m;i++){
w=getchar();
sum[w-'a']++;
for(int j=w-'a'-1;j>=0;j--)sum[j]=0;
}
for(int i=25;i>=0;i--){
while(sum[i]--)putchar(i+'a');
}
return 0;
}
T2 b
题意
给定一个长度为 \(N(N\le 5\times 10^6)\) 的序列。删除其中的一个元素需要花费的代价是其本身乘上与其相邻的
元素的乘积。请你求出将该序列删空的最小代价。
解法
首先我想到的是什么区间DP,但是没写出来;后来想到一个似乎是绝妙的优化,那就是每次删除只会删除左端点或者右端点,这样一定是最优的,因为这样一来,每次的代价就会是两个数相乘,而不是三个数连乘。
于是考试时打了个奇怪的线性DP,交上去,只有20分,实在不应该。
后来看了解法,发现是因为没有考虑到一些情况,比如对于序列 1 2 1
,它的最优解就是先删中间再删两边,答案为4 。为什么会造成这一情况呢,不是因为结论有问题,而是因为那个“两数乘积小于三数乘积”的结论在这三个数中有1时不再适用。于是用题解的话说,就是以1为分界把原序列割裂成许多小段,对每个小段用上面的结论快速求解。
由数据规模可知,对于每个小段应用 \(O(N)\) 的复杂度求解。发现按照上面那个方法可以得知,对于一个序列,先删最左边再删最右边与先删最右边再删最左边是一个效果,也就是说不论按照什么顺序删,代价都是一样的。这就符合线性求解的特征。
然后就可以了。最后说一下,由于每个序列删完之后最后会剩下一个元素,秉着贪心的原则,就选择每一段中最小的那个元素,累加它即可。
#include<cstdio>
#define int long long
const int N=5000010;
int m,ans,minn=1e18,a[N];
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
scanf("%lld",&m);
for(int i=1;i<=m;i++){
scanf("%lld",&a[i]);
if(a[i]==1)ans++;
}
int l=1,r=1;
while(l<m){
while(l<=m&&a[l]==1)l++;r=l;
while(r<=m&&a[r]!=1)r++;r--;
for(int i=l;i<=r;i++)minn=minn>a[i]?a[i]:minn;
for(int i=l+1;i<=r;i++)ans+=a[i]*a[i-1];
ans+=minn;minn=1e18;l=r+1;
}
printf("%lld",ans);
return 0;
}
T3 c
题意
给定一张 N 个点 M 条边且边带权的无向图,你可以选择任意一条 S 到 T 之间的最短路径,使得这条路径上的所有边权变为 0。求出进行修改之后,X 到 Y 之间路径长度的可能的最小值是多少。Q 次询问。
\(N,M\le 10^5,Q\le 10\)
解法
暴力枚举即可,复杂度很假,但由于数据过水可以卡过。仅供参考。正解是建最短路图(建出来是个DAG)再进行拓扑DP,但我没写。
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
//#define zczc
#define ll long long
#define int long long
using namespace std;
const int N=100010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
int m,n,r,l,s,t;
struct edge{
int t,v,next;
}e[N<<4];
int esum,head[N];
inline void add(int fr,int to,int val){
esum++;
e[esum].t=to;
e[esum].v=val;
e[esum].next=head[fr];
head[fr]=esum;
return;
}
vector<int>mem[N];
ll dis[N];
bool vis[N];
struct node{
int wh;ll dis;
};
bool operator <(node s1,node s2){
return s2.dis<s1.dis;
}
priority_queue<node>q;
int st[N],top;
int phead[N];
ll ans=1e18;
void check(){
int pesum=esum;
for(int i=1;i<=m;i++)phead[i]=head[i];
for(int i=2;i<=top;i++){
add(st[i],st[i-1],0);
add(st[i-1],st[i],0);
}
memset(dis,0x3f,sizeof(dis));
memset(vis,false,sizeof(vis));
dis[s]=0;
q.push((node){s,0});
while(!q.empty()){
int wh=q.top().wh;ll nd=q.top().dis;q.pop();
if(vis[wh])continue;vis[wh]=true;
for(int i=head[wh],th;i;i=e[i].next){
th=e[i].t;
if(dis[wh]+e[i].v<dis[th]){
dis[th]=dis[wh]+e[i].v;
q.push((node){th,dis[th]});
}
}
}
//for(int i=1;i<=top;i++)printf("%lld ",st[i]);printf("\n");
if(dis[t]<ans)ans=dis[t];
esum=pesum;
for(int i=1;i<=m;i++)head[i]=phead[i];
return;
}
void dfs(int wh){
st[++top]=wh;
if(wh==l){
check();
return;
}
for(int i=0;i<mem[wh].size();i++){
dfs(mem[wh][i]);
}
top--;
return;
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);read(n);
read(l);read(r);
read(s);read(t);
int s1,s2,s3;
for(int i=1;i<=n;i++){
read(s1);read(s2);read(s3);
add(s1,s2,s3);add(s2,s1,s3);
}
memset(dis,0x3f,sizeof(dis));
dis[l]=0;
q.push((node){l,0});
while(!q.empty()){
int wh=q.top().wh;ll nd=q.top().dis;q.pop();
if(vis[wh])continue;vis[wh]=true;
for(int i=head[wh],th;i;i=e[i].next){
th=e[i].t;
if(dis[wh]+e[i].v<dis[th]){
mem[th].clear();
mem[th].push_back(wh);
dis[th]=dis[wh]+e[i].v;
q.push((node){th,dis[th]});
}
else if(dis[wh]+e[i].v==dis[th]){
mem[th].push_back(wh);
}
}
}
dfs(r);
printf("%lld",ans);
return 0;
}