ps:这个时候蓝桥已经结束一个星期了。。我才把这个训练的题补完。
D-P8715 [蓝桥杯 2020 省 AB2] 子串分值 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:对于每一个位置的字母,预处理出这个字母左边第一个出现的下标,和右边第一个出现的下标。
对应这个位置字母的贡献为,左边可选个数+右边可选个数+左边可选个数*右边可选个数。 左边可选和右边可选的个数都可以通过查询预处理的下标数组,计算得出。
void solve(){ //D //思维
string str; cin>>str;
int n=str.size();
int arrL[n],arrR[n];
int idxL[26],idxR[26];
for(int i=0;i<26;i++) idxL[i]=-1,idxR[i]=-1; //init
for(int i=0;i<n;i++){
int c=str[i]-'a';
arrL[i]=idxL[c];
idxL[c]=i;
}
for(int i=n-1;i>=0;i--){
int c=str[i]-'a';
arrR[i]=idxR[c];
idxR[c]=i;
}
int ans=n;
for(int i=0;i<n;i++){
int numL,numR;
if(arrL[i]==-1) numL=i;
else numL=i-arrL[i]-1;
if(arrR[i]==-1) numR=n-i-1;
else numR=arrR[i]-i-1;
ans+=numL+numR+numL*numR;
}
cout<<ans;
}
ps:这题赛时是写出来了,但是还是值得贴在这里。
E-P8712 [蓝桥杯 2020 省 B1] 整数拼接 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:预处理。切记!!此题要处处取模,不然会爆。
int cnt1[12][100005];
void solve(){ //E 经典 数字拼接题
int n,k; cin>>n>>k;
// int f1[12]={0},f2[12]={0}; //f1含义为数字长度为i,且是k倍数的数字的个数;f2含义为,数字乘以10^i之后是k的倍数的个数--因为数字最大到1e9,开12即可
int arr[100005];
// map<pair<int,int>,int> mp1,mp2; //m1p:<x,y>,z----乘10^x后余k为y的数有z个;;;;mp2;<x,y>,z----数字长度为x,余k后为y的数字有z个
for(int i=1;i<=n;i++){
cin>>arr[i];
int x=arr[i]%k;
for(int j=1;j<=10;j++) cnt1[j][(x*(int)pow(10,j))%k]++;
// mp2[{(int)log10(x)+1,x%k}]++;
// for(int j=1;j<10;j++) mp1[{j,(x*(int)pow(10,j))%k}]++;
}
int ans=0;
for(int i=1;i<=n;i++){
int num=(int)log10(arr[i])+1;
int x=arr[i]%k;
if(x==0) ans+=cnt1[num][0];
else ans+=cnt1[num][k-x];
}
for(int i=1;i<=n;i++){ //减去自己拼自己的
int num=log10(arr[i])+1;
if( (arr[i]%k*(int)pow(10,num)+arr[i])%k==0 ) ans--;
}
cout<<ans;
}
F-P8725 [蓝桥杯 2020 省 AB3] 画中漂流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:线性dp!
int dp[3005][2005]={0}; //时间到i秒,剩下j体力
void solve(){ //F
int d,t,m; cin>>d>>t>>m; //往下游走>=d就死---坚持t秒---可向上m次
dp[0][m]=1;
for(int i=1;i<=t;i++){
for(int j=0;j<=m;j++){
if( (m-j)-(i-(m-j))+d<=0 ) continue; //m-j为前进,i-(m-j)为后退
dp[i][j]=(dp[i-1][j]+dp[i-1][j+1])%1000000007;
}
}
cout<<dp[t][0];
}
H-P8703 [蓝桥杯 2019 国 B] 最优包含 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:dp。
int dp[1003][1003]; //考虑前i个s的字符,包含t中前j个字符所需的代价
void solve(){ //H
string s,t; cin>>s>>t;
memset(dp,0x3f3f3f3f,sizeof(dp));
for(int i=0;i<s.size();i++) dp[i][0]=0;
for(int i=0;i<s.size();i++){
for(int j=0;j<=i;j++){ //dp数组整体位移一位
if(s[i]!=t[j]) dp[i+1][j+1]=min(dp[i][j]+1,dp[i][j+1]); //修改 或 不修改
else dp[i+1][j+1]=dp[i][j];
}
}
cout<<dp[s.size()][t.size()];
}
I-P8710 [蓝桥杯 2020 省 AB1] 网络分析 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:并查集+懒标记。在两个节点合并(即两个集合合并之前),把懒标记加到各自集合中,再进行合并。懒标记加完要清零,且最后还要下放剩余的懒标记到其集合中。
ps:196ms;102.69MB
int n,m;
int fa[10004],lag[10004],ans[10004];
vector<int> sett[10004];
int find(int x){
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) fa[i]=i,sett[i].emplace_back(i);
for(int i=1;i<=m;i++){
int op,x,y; cin>>op>>x>>y;
if(op==1){ //每次连接节点之前,下放标记。
if(x>y) swap(x,y);
int fa1=find(x),fa2=find(y);
if(fa1!=fa2){
if(lag[fa1]!=0) for(auto v:sett[fa1]) ans[v]+=lag[fa1]; //把懒标记下放到其集合中
lag[fa1]=0;
if(lag[fa2]!=0) for(auto v:sett[fa2]) ans[v]+=lag[fa2];
lag[fa2]=0;
for(auto v:sett[fa2]) sett[fa1].emplace_back(v);
sett[fa1].emplace_back(y); //fa1这个集合中加入点y--wa!!!这样只是添加了一个点,y集合的其他点会被丢弃!!
fa[fa2]=fa1;
}
}
else if(op==2) lag[find(x)]+=y; //把标记加到父结点
}
for(int i=1;i<=n;i++){
if(lag[i]!=0){
for(auto v:sett[i]) ans[v]+=lag[i];
}
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}
ps:929ms;1.37MB
int n,m;
int fa[10004],lag[10004],ans[10004];
vector<int> sett[10004];
int find(int x){
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) fa[i]=i,sett[i].emplace_back(i);
for(int i=1;i<=m;i++){
int op,x,y; cin>>op>>x>>y;
if(op==1){
if(x>y) swap(x,y);
int fa1=find(x),fa2=find(y);
if(fa1!=fa2){
for(int i=1;i<=n;i++) ans[i]+=lag[find(i)];
for(int i=1;i<=n;i++) lag[i]=0;
sett[fa1].emplace_back(y);
fa[fa2]=fa1;
}
}
else if(op==2) lag[find(x)]+=y;
}
for(int i=1;i<=n;i++) cout<<ans[i]+lag[find(i)]<<" ";
}
ps:自己写的在时间上更优于普通的写法。因为避免了很多不必要的lag的遍历。普通写法是只要集合合并,就下放全部懒标记。自己写的是合并哪两个集合,就下放哪两个集合的懒标记。
J-P8724 [蓝桥杯 2020 省 AB3] 限高杆 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:法一--分层建图,建一个三层的图,分别表示恢复0条边,恢复1条边,恢复2条边。共需要3倍空间。
法二:正常建图。动态规划思想--dis[10004][3]--表示到达点 i,恢复j(0,1,2)条边时的最短路。
法一:204ms;17.16MB
int n,m;
const int inf=0x3f3f3f3f;
vector<pair<int,int>> vct[30004];
priority_queue<pair<int,int>> pq;
int dis[30004],vis[30004];
void dijkstr(int s){
memset(dis,inf,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
pq.emplace(0,s);
while(pq.size()){
int from=pq.top().second;
pq.pop();
if(vis[from]) continue;
vis[from]=1;
for(auto v:vct[from]){
int to=v.first,weight=v.second;
if(dis[to]>dis[from]+weight){
dis[to]=dis[from]+weight;
pq.emplace(-dis[to],to);
}
}
}
}
void solve(){ //分层图!! !法一!
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w,c; cin>>u>>v>>w>>c;
//用以下方式建一个立体的三层图,跑正常的dijkstra即可。恢复一条边的最短距离为dis[2*n];恢复两条边的最短距离为dis[3*n];
//此题恢复的边一定会用上那条边,不一定恢复两条边会比恢复一条边短,或比不恢复边短。取min即可。
if(c==0){ //没有障碍,三层都在本层正常建边
vct[u].emplace_back(v,w); //第一层
vct[v].emplace_back(u,w);
vct[u+n].emplace_back(v+n,w); //第二层
vct[v+n].emplace_back(u+n,w);
vct[u+2*n].emplace_back(v+2*n,w); //第三层
vct[v+2*n].emplace_back(u+2*n,w);
}
else{ //有障碍,第一层连向第二层,第二层连向第三层。
// vct[u].emplace_back(v+n,w); //这4行是错的。
// vct[v+n].emplace_back(u,w);
// vct[u+n].emplace_back(v+2*n,w);
// vct[v+2*n].emplace_back(u+n,w);
vct[u].emplace_back(v+n,w); //单向的!!一层向下一层!!,否则可能会从下层更新上层的点
vct[v].emplace_back(u+n,w);
vct[u+n].emplace_back(v+2*n,w);
vct[v+n].emplace_back(u+2*n,w);
}
}
dijkstr(1);
cout<<dis[n]-min({dis[n],dis[2*n],dis[3*n]}); //不一定恢复的边越多越短!直接取三个的最小值准没错!!
}
法二:148ms;10.16MB
int n,m;
typedef struct myp1{
int v,w,typ;
}myp1;
typedef struct myp2{
int d,node,take;
friend bool operator <(const myp2 a,const myp2 b){ //只能重载小于号!!
return a.d>b.d; //按d的从小到大排序
}
}mpy2;
const int inf=0x3f3f3f3f;
vector<myp1> vct[10004];
priority_queue<myp2> pq;
//priority_queue<pair<int,pair<int,int>>> pq;
int dis[10004][3],vis[10004][3];
void dijkstra(int s){
memset(dis,inf,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s][0]=0;
pq.emplace((myp2){0,s,0});
while(pq.size()){
int from=pq.top().node;
int take=pq.top().take;
pq.pop();
if(vis[from][take]) continue;
vis[from][take]=1;
for(auto v:vct[from]){
int to=v.v,weight=v.w,typ=v.typ;
if(take+typ>2) continue;
if(dis[to][take+typ]>dis[from][take]+weight){
dis[to][take+typ]=dis[from][take]+weight;
pq.emplace(myp2{dis[to][take+typ],to,take+typ});
}
}
}
}
void solve(){ //不建分层图,用动态规划思想.
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w,t; cin>>u>>v>>w>>t;
vct[u].emplace_back((myp1){v,w,t});
vct[v].emplace_back((myp1){u,w,t});
}
dijkstra(1);
cout<<dis[n][0]-min({dis[n][0],dis[n][1],dis[n][2]}); //不一定恢复的边越多越短!直接取三个的最小值准没错!!
}
ps:似乎动态规划思路在时间空间上都略优于分层图。