something about Educational Codeforces Round 11

来源:互联网 发布:linux vi 不保存退出 编辑:程序博客网 时间:2024/06/16 02:32

  这场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 longconst 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的式子,其中xy由右端点决定,k由左端点决定。最优的合并方案是枚举左边,然后利用右边的凸壳迅速得到每个左端点对应的最优右端点。最近怎么写什么题都要凸壳= =

#include <bits/stdc++.h>using namespace std;#define ll long longconst 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;}
0 0
原创粉丝点击