CF2027D2 The Endspeaker (Hard Version) 题解
Super_Cube · · 题解
Solution
注意到
贪心想不出来就来想 dp。设
这样做答案是对了,但是方案会有缺漏。
问题出在每次可以不用转移极大连续段,只选取其中的一小段前缀进行转移,即对于
区间更新,单点查询,直接上线段树维护转移。
时间复杂度:
Code
极其丑陋。
#include<bits/stdc++.h>
typedef long long ll;
typedef std::pair<ll,int> pli;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
inline void operator+=(pli&x,const pli&y){
if(x.first!=y.first){
if(x.first>y.first)x=y;
}else if((x.second+=y.second)>=mod)
x.second-=mod;
}
struct segment{
int l,r;
pli v;
}t[1200005];
void build(int p,int l,int r){
t[p].l=l;t[p].r=r;
t[p].v.first=inf;t[p].v.second=0;
if(l==r)return l==1?t[p].v.first=0,t[p].v.second=1:0,void();
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
inline void tag_down(int p){
if(t[p].v.first!=inf)
t[p<<1].v+=t[p].v,t[p<<1|1].v+=t[p].v,t[p].v.first=0x3f3f3f3f3f3f3f3f,t[p].v.second=0;
}
void upd(int p,int l,int r,const pli&x){
if(t[p].l>=l&&t[p].r<=r)return t[p].v+=x;
tag_down(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid)upd(p<<1,l,r,x);
if(r>mid)upd(p<<1|1,l,r,x);
}
pli ask(int p,int x){
if(t[p].l==t[p].r)return t[p].v;
tag_down(p);
return ask(p<<1|(x>(t[p].l+t[p].r>>1)),x);
}
std::vector<ll>a;
std::vector<int>b;
int T,n,m;
std::pair<ll,int>ans;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
a.resize(n+1);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]),a[i]+=a[i-1];
b.resize(m+1);
for(int i=1;i<=m;++i)
scanf("%d",&b[i]);
build(1,1,n);
ans.first=inf;ans.second=0;
for(int j=1;j<=m;++j){
static pli res,val;
res.first=inf;res.second=0;
for(int i=1,k;i<=n;++i){
k=std::upper_bound(a.begin()+i,a.begin()+n+1,b[j]+(i?a[i-1]:0))-a.begin();
if(i<k){
val=ask(1,i);val.first+=m-j;
if(k==n+1)res+=val,--k;
if(i<k)upd(1,i+1,k,val);
}
}
ans+=res;
}
if(ans.first==0x3f3f3f3f3f3f3f3f)puts("-1");
else printf("%lld %d\n",ans.first,ans.second);
}
return 0;
}
好笑的是 D2 跑得比 D1 快。