这场CF的E、F两题最近才补,拿来说说。比较有意思的是,官方题解给的都不是最优复杂度。。
E. Different Subsets For All Tuples
这道题肯定是dp,我的做法是这样的。假设你现在拥有了一个长度为
i
的,由
由于每次可以添加
m
种字符,所以长度每增加1,答案将乘上
但是到底有多少重复计数了呢,可以发现,
m
种可添加的字符里面,有
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9+7;
ll dp[1000010];
int main(){
int n,m;
cin>>n>>m;
dp[0] = 1;
ll sub = 0;
for(int i=1;i<=n;i++){
dp[i] = dp[i-1] * m * 2;
sub *= (m-1);
if(i>1){
sub += dp[i-2];
}
sub %= mod;
dp[i] -= sub * m;
dp[i] %= mod;
}
cout<<(dp[n] + mod)%mod<<endl;
return 0;
}
F. Bear and Bowling 4
分治,合并的复杂度,就是计算起点和终点横跨左右两半的复杂度。对于给定的左右端点,它的答案是一个形如
x+k∗y
的式子,其中
x
和
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 200010;
const ll INF = 2e18;
ll a[maxn];
ll sum[maxn];
ll incSum[maxn];
ll sufSum[maxn];
struct Point{
ll x;
ll y;
int id;
Point(ll x,ll y,int id):x(x),y(y),id(id){
}
Point(){
}
bool operator<(const Point& other)const{
if(x!=other.x){
return x<other.x;
}
return y>other.y;
}
}pts[maxn];
Point ch[maxn];
int sz;
ll solve(int l,int r){
if(l==r){
return a[l];
}
int mid = (l+r)>>1;
ll res = max(solve(l,mid),solve(mid+1,r));
int k = 1;
for(int i=mid+1;i<=r;i++,k++){
pts[k].x = pts[k-1].x + a[i];
pts[k].y = pts[k-1].y + a[i] * k;
pts[k].id = i;
}
sort(pts+1,pts+k);
//Convex Hull
sz = 0;
for(int i=1;i<k;i++){
if(i>1 && pts[i].x == pts[i-1].x){
continue;
}
if(sz<2){
ch[sz++] = pts[i];
}else{
while(sz>1 && (pts[i].y-ch[sz-1].y+0.0)*(ch[sz-1].x-ch[sz-2].x) >=
(ch[sz-1].y-ch[sz-2].y+0.0)*(pts[i].x-ch[sz-1].x) ){
sz--;
}
ch[sz++] = pts[i];
}
}
ll part1 = 0;
int best = 0;
for(int i=mid;i>=l;i--){
part1 += sufSum[mid] - sufSum[i-1];
ll C = (mid - i + 1);
ll part2 = -INF;
ll cur;
while(best<sz){
cur = ch[best].x*C + ch[best].y;
if(cur > part2){
part2 = cur;
best++;
}else{
break;
}
}
best--;
res = max(res,part1+part2);
}
return res;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%I64d",&a[i]);
sufSum[i] = sufSum[i-1] + a[i];
}
ll ans = solve(1,n);
if(ans < 0) ans = 0;
cout<<ans<<endl;
return 0;
}