ABC-255
作者:互联网
E - Lucky Numbers
Problem
给定长度为\(N-1\)的序列\(S\),长度为\(N\)的序列\(A\)定义为\(A_i+A_{i+1}=S_i\),现在有\(M\)个幸运数字\(X_i\),问怎样的序列\(A\)可以使得序列\(A\)中包含的幸运数字最多,输出这个最多的个数。
\(1\le N\le 2\times 10^5\),\(1\le M\le 10\)
Solve
把\(A_i\)展开,发现\(A_i=S_i'+(-1)^iA_1\),那么假设\(A_i\)是一个幸运数字,那么\((-1)^iA_1=A_i-S_i'\)
确定\(A_1\)取哪个值的时候,会产生最多的幸运数,不需要枚举\(A_i\),也没办法枚举,但可以通过幸运数取计算\(A_1\)的值,然后统计可以由幸运数字得到的最多的\(A_1\),最多的\(A_1\)的个数就是答案。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
vector<ll>s(n+1),x(m+1);
for(int i=1;i<n;i++) cin>>s[i];
for(int i=1;i<=m;i++) cin>>x[i];
map<ll,ll>cnt;
auto cal=[&](ll pre,int xs)->void{
if(xs==1){
for(int i=1;i<=m;i++) cnt[x[i]-pre]++;
}else{
for(int i=1;i<=m;i++) cnt[pre-x[i]]++;
}
} ;
ll pre=0;
cal(0,1);
for(int i=1,xs=1;i<n;i++)
{
xs=-xs;
pre=s[i]-pre;
cal(pre,xs);
}
ll ans=0;
for(auto [p,c]:cnt){
ans=max(ans,c);
}
cout<<ans<<'\n';
}
F - Pre-order and In-order
Problem
给定二叉树的前序遍历和中序遍历,求二叉树的形态
Solve
分治做法,按照前序遍历的顺序,当前前序遍历的值就是当前递归子树的根\(rt\),在中序遍历中找到当前的根的位置记为\(p\),那么这棵子树的左右儿子坑定在\(p\)的左右连边,记录一下边界即可。
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
vector<int>pre(n+1);
vector<int>mid(n+1);
vector<int>pos(n+1);
for(int i=1;i<=n;i++) cin>>pre[i];
for(int i=1;i<=n;i++) cin>>mid[i],pos[mid[i]]=i;
vector<int>L(n+1),R(n+1);
if(pre[1]!=1){
puts("-1");
return 0;
}
//S,T为中序遍历的边界,s,t为前序遍历的边界
auto dfs=[&](auto self,int s,int t,int S,int T)->bool{
int r=pre[s],p=pos[r];
if(p<S||T<p) return false;
if(p-S>0){ //p-S即为左子树大小
L[r]=pre[s+1];
if(!self(self,s+1,s+p-S,S,p-1)) return false;
}
if(T-p>0){//T-p即为右子树大小
R[r]=pre[s+p-S+1];
if(!self(self,s+p-S+1,t,p+1,T)) return false;
}
return true;
};
if(!dfs(dfs,1,n,1,n)){
cout<<-1<<'\n';
return 0;
}
for(int i=1;i<=n;i++) cout<<L[i]<<" "<<R[i]<<'\n';
return 0;
}
G - Constrained Nim (博弈论、优化)
Problem
Solve
Code
Ex - Range Harvest Query(珂朵莉树)
Problem
有\(N\)棵果树,编号从\(1\)到\(N\),第\(i\)个果树每天会长出\(i\)个果子。现在有\(Q\)次操作,每次操作给出三个整数\(L_i,R_i,D_i\),保证\(D_i\)严格递增,表示第\(D_i\)天会在区间\(L_i,R_i\)把果树上的水果全部摘完,每次你需要回答出摘了多少个果子。
\(1\le N\le 10^{18}\),\(1\le Q\le 2\times 10^5\),$1\le D_1\lt D_2\lt \cdot\cdot\cdot\lt D_Q\le 10^{18} $
Solve
每个区间记录上一次被采摘的时间\(pt\),然后如果遇到最新的采摘时间\(nt\),那么这个区间的贡献就是\(\frac{(r+1)(r-l+1)}{2}(nt-pt),每次合并过程中删除线段的时候统计贡献即可\)。
Code
#include <bits/stdc++.h>
#define fi first
#define se second
#define ll long long
using namespace std;
const int mod=998244353;
struct ODT{
map<pair<ll,ll>,ll>seg;//左闭右开的区间
ll L,R;
ODT(ll L_,ll R_){
seg[{L_-1,L_-1}]=-1;
seg[{R_+1,R_+1}]=-1;
seg[{L_,R_}]=0;
L=L_-1,R=R_+1;
}
//delseg、addseg写自己在删除或添加线段是要统计的信息,自己传入函数
void assign(ll l,ll r,ll v,function<void(ll,ll,ll)>delseg=[](ll l,ll r,ll v){},
function<void(ll,ll,ll)>addseg=[](ll l,ll r,ll v){}){
auto split=[&](ll x){
auto p=prev(seg.lower_bound({x,L}));
if(p->fi.se>x){
ll l=p->fi.fi,r=p->fi.se,v=p->se;
seg.erase(p);
seg[{l,x}]=v;
seg[{x,r}]=v;
}
};
split(l),split(r);//分开两头的区间
auto pl=seg.lower_bound({l,L});
//合并中间的区间
while(1){
auto pr=pl;
pr++;
if(pl->fi.fi>=r) break;
delseg(pl->fi.fi,pl->fi.se,pl->se);
seg.erase(pl);
pl=pr;
}
addseg(l,r,v);
seg[{l,r}]=v;
}
};
ll ans;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll n;
int q;
cin>>n>>q;
ODT t(1,n+1);
while(q--)
{
ll d,l,r;
cin>>d>>l>>r;
ans=0;
++r;
t.assign(l,r,d,
[&](ll l,ll r,ll v){
l%=mod,r%=mod;
ans=(ans+1LL*(l+r-1)*(r-l)/2%mod*((d-v+mod)%mod)%mod)%mod;
},
[&](ll l,ll r,ll v){} );
if(ans<0) ans=ans+mod;
cout<<ans<<'\n';
}
}
标签:ABC,int,ll,seg,le,fi,mod,255 来源: https://www.cnblogs.com/Arashimu0x7f/p/16392878.html