Codeforces Round #343 (Div. 2)

C. Famil Door and Brackets

dp。状态 dp(i,j,k) i 表示填充到了第几个括号,j表示当前左括号比右括号多多少, k 表示当前在那个子串的左边还是右边填充,状态转移见代码。要注意一些情况,比如要保证任何时候前缀的左括号不少于右括号,后缀的右括号不少于左括号。还有当n=m时,判断子串的合法性。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const int mod = 1e9+7;

char str[100010];

ll dp[2010][2010][2];

int main(){
    int n,m;
    while(cin>>n>>m){
    memset(dp,0,sizeof(dp));
    scanf("%s",str);
    int lcnt=0;
    int rcnt=0;
    int cnt=0;
    int MAX=-1e9;
    int MAX2=-1e9;

    for(int i=m-1;i>=0;i--){
        if(str[i]=='('){
            cnt++;
        }else{
            cnt--;
        }
        MAX2=max(MAX2,cnt);
    }

    cnt=0;
    bool valid = 1;
    for(int i=0;i<m;i++){
        if(str[i]=='('){
            lcnt++;
            cnt--;
        }else{
            rcnt++;
            cnt++;
        }
        if(rcnt>lcnt)valid=0;
        MAX=max(MAX,cnt);
    }

    int d=n-m;
    if(lcnt+d<rcnt){
    }else if(rcnt+d<lcnt){
    }else{
        dp[0][0][0]=1;
        for(int i=1;i<=d;i++){
            for(int j=0;j<=d;j++){
                if(d-i>=MAX2 && j<d){
                    dp[i][j+1][0] += dp[i-1][j][0];
                    dp[i][j+1][0] %= mod;
                }
                if(d-i>=MAX2 && j>0){
                    dp[i][j-1][0] += dp[i-1][j][0];
                    dp[i][j-1][0] %= mod;
                }

                if(j>=MAX && j+lcnt-rcnt+1<=d && j+lcnt-rcnt+1>=0){
                    dp[i][j+lcnt-rcnt+1][1] += dp[i-1][j][0];
                    dp[i][j+lcnt-rcnt+1][1] %= mod;
                }
                if(j>=MAX && j+lcnt-rcnt-1<=d && j+lcnt-rcnt-1>=0){
                    dp[i][j+lcnt-rcnt-1][1] += dp[i-1][j][0];
                    dp[i][j+lcnt-rcnt-1][1] %= mod;
                }

                if(j<d){
                    dp[i][j+1][1] += dp[i-1][j][1];
                    dp[i][j+1][1] %= mod;
                }
                if(j>0){
                    dp[i][j-1][1] += dp[i-1][j][1];
                    dp[i][j-1][1] %= mod;
                }
            }
        }
    }

    ll ans = dp[d][0][1];
    if(rcnt-lcnt>=0 && rcnt-lcnt<=d){
        ans += dp[d][rcnt-lcnt][0];
    }
    ans %= mod;
    if(n==m && !valid)ans=0;
    cout<<ans<<endl;
    }
    return 0;
} 

D. Babaei and Birthday Cake

由于任意蛋糕的上方只能是编号更大的蛋糕,所以逆序处理。将蛋糕体积离散化,每次计算用新的蛋糕作为“底座”时最大总体积,方法是线段树区间更新单点查询,线段树的节点维护对应体积为“底座”时的最大总体积,每次拿合法的最大的总体积叠在上面。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const int mod = 1e9+7;
const int maxn = 100010;
const double PI = 3.14159265358979323846264;


ll r[maxn];
ll h[maxn];

ll v[maxn];

struct node{
    int l,r;
    ll val;
}tree[maxn<<2];

void build_tree(int rt,int l,int r){
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].val = 0;
    if(l==r)return;
    int mid = (l+r)>>1;
    build_tree(rt<<1,l,mid);
    build_tree((rt<<1)|1,mid+1,r);
}

void update(int rt,int l,int r,ll val){
    if(tree[rt].l == l && tree[rt].r == r){
        tree[rt].val = max(val,tree[rt].val);
        return;
    }
    int mid = (tree[rt].l + tree[rt].r)>>1;
    if(r<=mid){
        update(rt<<1,l,r,val);
    }else{
        if(l>mid){
            update((rt<<1)|1,l,r,val);
        }else{
            update(rt<<1,l,mid,val);
            update((rt<<1)|1,mid+1,r,val);
        }
    }
}

ll query(int rt,int x){
    if(tree[rt].l == tree[rt].r){
        return tree[rt].val;
    }
    int mid = (tree[rt].l+tree[rt].r)>>1;
    if(x<=mid){
        return max(tree[rt].val,query(rt<<1,x));
    }else{
        return max(tree[rt].val,query((rt<<1)|1,x));
    }
}

map<ll,int> mp;

int main(){
    int n;
    cin>>n;
    for(int i = 1;i<=n;i++){
        scanf("%I64d %I64d",&r[i],&h[i]);
        v[i] = r[i]*r[i]*h[i];
        mp[v[i]] = 0;
    }

    int k=1;
    for(auto it=mp.begin();it!=mp.end();it++){
        it->second = k++;
    }

    build_tree(1,1,k);

    double ans = 0;
    for(int i=n;i>0;i--){
        int id = mp[v[i]];  
        double tmp = query(1,id+1);
        tmp+=v[i];
        update(1,1,id,tmp);
        ans=max(ans,tmp);
    }
    printf("%.12f\n",ans*PI);
    return 0;
} 

E. Famil Door and Roads

解题核心是求LCA。先分析下要使u-v成环需要满足什么条件:u-v在树上有且仅有一条简单路径,新的边只要加在u-v两个端点的两头,就能成环。所以我们只要计算两头所有点到u/v的平均距离即可。分两种情况,若lca不是u或v,两端的点就是u和v的子树;否则,假设v是u和v的lca,u那头的点还是u的子树,v那头的点则是整棵树砍掉v往u方向的孩子。在dfs时,去计算每个点到其子孙的总距离和到所有点的总距离,就可以进而算出上述平均距离。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const int maxn = 100010;

int head[maxn];
int pre[maxn<<1];
int Next[maxn<<1];
int tot;
int p[maxn][20];
int dep[maxn];
int size[maxn];

double dist_down[maxn];
double dist_all[maxn];

void init(){
    memset(head,-1,sizeof(head));
    dep[1]=1;
    tot=0;
}

void dfs(int u,int fa){
    //
    int tmp = fa;
    for(int i=0;i<=16;i++){
        p[u][i] = tmp;
        tmp = p[tmp][i];
    }
    //
    size[u]=1;
    for(int i=head[u];~i;i=pre[i]){
        int v=Next[i];
        if(v==fa)continue;
        dep[v]=dep[u]+1;
        dfs(v,u);
        size[u]+=size[v];
        dist_down[u] += (dist_down[v]+size[v]);
    }
}

void dfs2(int u,int fa){
    if(u==1){
        dist_all[u] = dist_down[u];
    }else{
        dist_all[u] = dist_all[fa] - size[u] + (size[1]-size[u]);
    }

    for(int i=head[u];~i;i=pre[i]){
        int v=Next[i];
        if(v==fa)continue;
        dfs2(v,u);
    }
}

int get_ancestor(int u,int x){
    int k = 0;
    while(x){
        if(x&1){
            u=p[u][k];
        }
        k++;
        x>>=1;
    }
    return u;
}

int lca(int u,int v){
    if(dep[v]>dep[u]){
        swap(u,v);
    }

    u=get_ancestor(u,dep[u]-dep[v]);
    if(u==v)return u;
    int k=16;
    while(k>=0){
        if(p[u][k]!=p[v][k]){
            u = p[u][k];
            v = p[v][k];
        }
        k--;
    }
    return p[u][0];
}

void addEdge(int u,int v){
    Next[tot]=v;
    pre[tot]=head[u];
    head[u]=tot++;
    //
    Next[tot]=u;
    pre[tot]=head[v];
    head[v]=tot++;
}

int main(){
    init();

    int n,m;
    cin>>n>>m;

    int test =0;
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        if(i==1)test=u;
        addEdge(u,v);
    }

    dfs(1,0);
    dfs2(1,0);

    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        if(dep[u]<dep[v]){
            swap(u,v);
        }
        int LCA = lca(u,v);
        double ans = 0;

        if(LCA==v){
            int son = get_ancestor(u,dep[u]-dep[v]-1);
            ans = dist_down[u]/size[u] + (dist_all[v]-dist_down[son]-size[son])/(size[1]-size[son]);
            ans += (dep[u]-dep[v]+1);
        }else{
            ans = dist_down[u]/size[u] + dist_down[v]/size[v];
            ans += (dep[u]+dep[v]-dep[LCA]*2+1);
        }
        printf("%.10f\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值