2022.6.16补题
受到昨天14题里8题数学的影响,今天鸽一天gym,将这几天做的gym里面的银牌题(50<x<100)补一补。
2022 Jiangsu Collegiate Programming Contest
链接:https://codeforces.com/gym/103743
原来AC:A I K 罚时:207min

补题:C J
C:(单调队列)
单调队列前置知识点:
单调队列:
有n个生物,第i个生物会在第i到ai(i<=ai<=n)天出现(对于所有i(1<i<n),满足ai<=ai+1),它的攻击力是bi(1<=bi<=100000)。请输入每天出现的生物的攻击力最大值
思路:1.假设现在已经到了第i天,对于第j个生物(j<i),if(bj<bi),那么第j个生物不再考虑。
2.我们使用一个队列,按照编号从小到大的顺序,存一下到目前为止,哪些生物是需要被考虑的。
3.队列里面生物的攻击力是单调递减的,why?
Because 根据思路1,在队列中不可能存在后面的比前面的强,那么前面的那个不会入队
So,每次攻击力最高的都在队首。
opeartion:
1.加入一个生物,队列末尾比它攻击力低的生物不用考虑;
2.把队列末尾比它攻击力低的生物删除以后,加入队尾;
3.队首消失,则删除队首
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef double db; const int maxn=1e5+30; int n,a[maxn],b[maxn],c[maxn][2];//c数组模拟队列 int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]>>b[i]; int k=0,l=1;//l为头指针,k为尾指针 for(int i=1;i<=n;i++){ for(;k>=l&&b[i]>=c[k][0];k--);//把队列末尾比它攻击力低的生物删除 c[++k][0]=b[i];//++k,队尾加一位 c[k][1]=a[i];//记录状态 cout<<c[l][0]<<"\n"; for(;k>=l&&c[l][1]==i;l++);//l++就是删除队首,看队首元素是否消失 } return 0; }
C的题面:
Tom最近喜欢玩电子游戏。游戏规则如下:
游戏在x轴上进行。游戏中一共有n+1根柱子,被从左到右放置。柱子的编号从0~n。编号为i的支柱坐标为x=i。也有范围在[n+1,inf]的无限大的平台。玩家如果跳到这块区域里面就算胜利。
玩家从0号柱子开始,并且只能从左向右跳跃。即玩家们的坐标必须一直增加。并且他只能跳到平台的柱子上,否则他会掉到空白区域,输掉游戏。此外,他的跳跃能力是有限的。每次跳跃的距离不超过p。
除了0号柱子,剩下的每根柱子上都会有一个宝箱。在i号柱子上有ai金币。但是,也有一些陷阱(ai<0),这样Tom会损失-ai金币。
游戏有n级。Tom只能跳到第i级以i的倍数编号的柱子上。现在有q个询问,每一个包含一个数x,询问Tom如果在x级获胜,他能取得的数量最多的金币。Tom得到负数个金币也是有可能的。
输入
n(柱子数量) q(问题数量) p(最长跳跃距离)
a1~an
q行,一行一个x
输出
得到的金币的最大数量
不能赢,cout<<"Noob"<<"\n";
思路:
C题的DP式子很简单,就是令f[i]表示在i位置的最大值,有f[i]=max{f[i−k⋅j]}(k⋅j≤p),其中j表示当且为j级。
注意:此题不能用线段树,多个logn会TLE。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef double db; const int maxn=1e6+30; const LL inf=1e18+30; vector<int> qry[maxn]; vector<LL> v; LL a[maxn],ans[maxn]; int qq[maxn]; int ll,rr;//l头指针,r尾指针 int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int n,q,p; cin>>n>>q>>p; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=q;i++){ int x; cin>>x; if(x>p) ans[i]=-inf; else qry[x].push_back(i); } for(int i=1;i<=n;i++){ if(qry[i].size()){//x>p不考虑直接cout<<"Noob"<<"\n"; v.clear(); v.push_back(0); for(int j=i;j<=n;j+=i) v.push_back(j); v.push_back(n+1); vector<LL> f(v.size()+30,-inf); f[0]=0; ll=0,rr=-1; qq[++rr]=0; for(int j=1;j<v.size();j++){ while(ll<=rr&&v[j]-v[qq[ll]]>p) ll++;//头指针+1,删除队首 f[j]=max(f[j],f[qq[ll]]+a[v[j]]);//DP式子 while(ll<=rr&&f[qq[rr]]<=f[j]) rr--;//尾指针-1,入队尾 qq[++rr]=j; } for(auto j:qry[i]) ans[j]=f[v.size()-1]; } } for(int i=1;i<=q;i++){ if(ans[i]==-inf) cout<<"Noob"<<"\n"; else cout<<ans[i]<<"\n"; } return 0; }
J
J(平衡树)(递推,数学优化)
一棵二叉树是超级平衡的如果T是空的或者同时满足以下三个条件:
1.左子树是超级平衡的
2.右子树是超级平衡的
3.左子树和右子树的节点数最多相差1个。
计算有n个节点的超级平衡树的数量,答案%(1<<64)。
输入
T个样例
n(树上的节点)
输出
超级平衡树的数量
思路:找递推关系
设f[x]是n个点的超级平衡树的个数,则:
x==0 f[x]=1;//empty
x为偶数:f[x]=2*f[(x/2-1]*f[x/2];
x为奇数:f[x]=f[(x-1)/2]*f[(x-1)/2];
数组不能开到1<<64,所以尝试数学方法

然后根据递推式子写出dfs函数。
注意此题的数据,要unsigned long long!!!
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; ull dfs(ull x,ull a,ull b,ull c){ if(x==0) return 0; if(x==1) return c; if(x&1) return dfs(x>>1,2*a+b,b,c+b); else return dfs(x>>1,a,a+b*2,c+a); } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int T; cin>>T; while(T--){ ull n; cin>>n; ull ans=1;//empty ull c=dfs(n,1,0,0); if(c>=64) ans=0; else ans<<=c; cout<<ans<<"\n"; } return 0; }
The 17th Heilongjiang Provincial Collegiate Programming Contest的L和2022 Hubei Provincial Collegiate Programming Contest的J均是字符串哈希,明天整理字符串哈希相关题目。(QAQ)
只剩下The 19th Zhejiang Provincial Collegiate Programming Contest的J题和2020-2021 ICPC - Gran Premio de Mexico - Repechaje的 C D E
下午完成了Java大作业,晚上准备休息换换脑子了,明天一套gym…………

浙公网安备 33010602011771号