something about Educational Codeforces Round 11

  这场CF的E、F两题最近才补,拿来说说。比较有意思的是,官方题解给的都不是最优复杂度。。

E. Different Subsets For All Tuples

  这道题肯定是dp,我的做法是这样的。假设你现在拥有了一个长度为 i 的,由m种字符(或部分)组成的串,然后在最后面添加一个字符’a’,会发生什么呢?我们令’a’具有一般性,用’x’代表除了’a’以外的字符。。比如当前串是”xxaxxxaxxx”,由于我们可以在该串的所有子序列后面选择添加或不添加这个’a’,相当于子序列个数翻倍了。但是实际上会造成一些重复,思考下发现只要减去原串中最后一个’a’之前的部分(”xxaxxx”)就好了。
  由于每次可以添加 m 种字符,所以长度每增加1,答案将乘上m,再乘上2,再减去那些重复计数的部分。
  但是到底有多少重复计数了呢,可以发现, m 种可添加的字符里面,有m1种并不会改变原来的最后一个’a’的位置,于是需要减去的部分每次需要乘上 m1 。这样就得到了递推方案。

#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+ky 的式子,其中 x y由右端点决定, k <script type="math/tex" id="MathJax-Element-11">k</script>由左端点决定。最优的合并方案是枚举左边,然后利用右边的凸壳迅速得到每个左端点对应的最优右端点。最近怎么写什么题都要凸壳= =

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值