la1la1la的四连做..

写在前面的..

今天来了一波很成功的la1la1la四连做..

还是同样的激情0.0..

还是熟悉的爆零..


t1 Codeforces 713C

先把 ai 减去 i 把问题转化成单调不降..

然后比较显而易见地就可以知道最终的序列的数一定是原序列中出现过的..

那么简单dp乱搞就可以了..

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const LL Maxn = 3010;
LL a[Maxn], b[Maxn], bl, n;
LL f[Maxn][Maxn];
LL _abs ( LL x ){ return x < 0 ? -x : x; }
LL _min ( LL x, LL y ){ return x < y ? x : y; }
int main (){
    //freopen ( "a.in", "r", stdin );
    //freopen ( "a.out", "w", stdout );
    LL i, j, k;
    scanf ( "%I64d", &n );
    for ( i = 1; i <= n; i ++ ){
        scanf ( "%I64d", &a[i] );
        a[i] -= i;
        b[i] = a[i];
    }
    sort ( b+1, b+n+1 );
    bl = unique ( b+1, b+n+1 ) - (b+1);
    memset ( f, 63, sizeof (f) );
    for ( i = 1; i <= bl; i ++ ){
        f[1][i] = _abs (b[i]-a[1]);
        f[1][i] = _min ( f[1][i], f[1][i-1] );
    }
    for ( i = 2; i <= n; i ++ ){
        for ( j = 1; j <= bl; j ++ ){
            f[i][j] = f[i-1][j] + _abs (b[j]-a[i]);
            f[i][j] = _min ( f[i][j], f[i][j-1] );
        }
    }
    printf ( "%I64d\n", f[n][bl] );
    return 0;
}

t2 救灾 CH Round #50 - 铜牌爷&&退役狗杯

比赛的时候想到一个类似于就是正解的做法.. 然而我并不能实现..

好悲催啊0.0..

首先这棵树肯定是小于等于20层的..(显而易见的啊..不信自己打表去..)

dp两个数组, f1x,i 表示 x 节点最近的水井在该子树内距离为i的最小花费, f2x,i 表示 x 节点最近的水井在该子树外距离为i的最小花费..

推啊推啊就行了0.0..

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 200010;
struct node {
    int y, next;
}a[Maxn]; int first[Maxn], len;
void ins ( int x, int y ){
    len ++;
    a[len].y = y;
    a[len].next = first[x]; first[x] = len;
}
int phi[Maxn], pr[Maxn], prl; bool v[Maxn];
int f1[Maxn][41], f2[Maxn][41], n, cost[Maxn];
int g[Maxn];
int _min ( int x, int y ){ return x < y ? x : y; }
void get (){
    int i, j;
    for ( i = 2; i <= n; i ++ ){
        if ( v[i] == false ){
            pr[++prl] = i;
            phi[i] = i-1;
        }
        ins ( phi[i], i );
        for ( j = 1; j <= prl && i*pr[j] <= n; j ++ ){
            v[i*pr[j]] = true;
            if ( i % pr[j] == 0 ){
                phi[i*pr[j]] = phi[i] * pr[j];
                break;
            }
            else phi[i*pr[j]] = phi[i] * phi[pr[j]];
        }
    }
}
void dfs ( int x ){
    int i, j, k;
    if ( first[x] == 0 ){
        f1[x][0] = g[x] = cost[0];
        for ( i = 0; i <= 40; i ++ ) f2[x][i] = cost[i];
        return;
    }
    for ( k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        dfs (y);
    }
    for ( i = 0; i <= 40; i ++ ){
        f1[x][i] = f2[x][i] = cost[i];
        int Min = 0x7fffffff;
        for ( k = first[x]; k; k = a[k].next ){
            int y = a[k].y;
            int t = _min ( g[y], f2[y][i+1] );
            f1[x][i] += t;
            f2[x][i] += t;
            if ( i > 0 ) Min = _min ( f1[y][i-1]-t, Min );
        }
        if ( i > 0 ) f1[x][i] += Min;
        g[x] = _min ( g[x], f1[x][i] );
    }
}
int main (){
    int i, j, k;
    scanf ( "%d", &n );
    get ();
    for ( i = 0; i < n; i ++ ) scanf ( "%d", &cost[i] );
    memset ( f1, 63, sizeof (f1) );
    memset ( f2, 63, sizeof (f2) );
    memset ( g, 63, sizeof (g) );
    dfs (1);
    printf ( "%d\n", g[1] );
    return 0;
}

t3 Express 【弱省胡策】Round #4

首先如果只能放置一个点的话那肯定是放在这棵树的重心吧0.0..

那么这道题就相当于把整棵树分成两块,两块的点分别去该块的重心..

所以我们就枚举断开的边然后乱搞就行了..

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 50010;
struct node {
    int y, next;
}a[Maxn*2]; int first[Maxn], len;
void ins ( int x, int y ){
    len ++;
    a[len].y = y;
    a[len].next = first[x]; first[x] = len;
}
int w[Maxn], size[Maxn], sum[Maxn], dep[Maxn];
int f1[Maxn], f2[Maxn], fa[Maxn][18];
int son1[Maxn], son2[Maxn];
int n;
void dfs1 ( int x, int f ){
    size[x] = w[x];
    dep[x] = dep[f]+1;
    fa[x][0] = f;
    for ( int i = 1; i <= 16; i ++ ){
        fa[x][i] = fa[fa[x][i-1]][i-1];
    }
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( y == f ) continue;
        dfs1 ( y, x );
        size[x] += size[y];
        if ( size[y] > size[son1[x]] ){ son2[x] = son1[x]; son1[x] = y; }
        else if ( size[y] > size[son2[x]] ) son2[x] = y;
        f1[x] += f1[y]+size[y];
    }
}
void dfs2 ( int x, int f ){
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( y == f ) continue;
        sum[y] = sum[x]+size[x]-size[y];
        f2[y] = f2[x]+sum[x]+f1[x]-f1[y]+size[x]-2*size[y];
        dfs2 ( y, x );
    }
}
bool v[Maxn], bo[Maxn];
int rt1, rt2, ss;
void fr ( int &rt, int x, int msize ){
    rt = 0; int ans;
    while (x){
        int ms, ne;
        if ( bo[son1[x]] == true ){ ne = son2[x]; ms = size[son2[x]]; }
        else if ( v[son1[x]] == true ){
            if ( size[son1[x]]-ss < size[son2[x]] ){ ne = son2[x]; ms = size[son2[x]]; }
            else { ne = son1[x]; ms = size[son1[x]]-ss; }
        }
        else { ne = son1[x]; ms = size[son1[x]]; }
        if ( v[x] == true ){ if ( msize-(size[x]-ss) > ms ) ms = msize-(size[x]-ss); }
        else if ( msize-size[x] > ms ) ms = msize-size[x];
        if ( ms < ans || rt == 0 ){ rt = x; ans = ms; }
        else break;
        x = ne;
    }
}
int anss;
int lca ( int x, int y ){
    int i;
    if ( dep[x] < dep[y] ) swap ( x, y );
    for ( i = 16; i >= 0; i -- ){
        if ( dep[fa[x][i]] >= dep[y] ){
            x = fa[x][i];
        }
    }
    if ( x == y ) return x;
    for ( i = 16; i >= 1; i -- ){
        if ( fa[x][i] != fa[y][i] ){
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return fa[x][0];
}
void dp ( int x, int f ){
    v[x] = true;
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( y == f ) continue;
        bo[y] = true;
        ss = size[y];
        fr ( rt1, 1, size[1]-size[y] );
        int ret1 = f1[rt1]+f2[rt1]-f1[y]-size[y]*(dep[rt1]+dep[y]-2*dep[lca(y,rt1)]);
        ss = 0;
        fr ( rt2, y, size[y] );
        int ret2 = f1[rt2]+f2[rt2]-f2[y]-sum[y]*(dep[rt2]-dep[y]);
        if ( ret1+ret2 < anss ) anss = ret1+ret2;
        //printf ( "%d %d %d\n", x, y, ret1+ret2 );
        bo[y] = false;
        dp ( y, x );
    }
    v[x] = false;
}
int main (){
    int i, j, k;
    scanf ( "%d", &n );
    for ( i = 1; i < n; i ++ ){
        int x, y;
        scanf ( "%d%d", &x, &y );
        ins ( x, y ); ins ( y, x );
    }
    for ( i = 1; i <= n; i ++ ) scanf ( "%d", &w[i] );
    dfs1 ( 1, 0 );
    dfs2 ( 1, 0 );
    anss = 0x7fffffff;
    dp ( 1, 0 );
    printf ( "%d\n", anss );
    return 0;
}

t4 某hdu题的改编..

用线段树保存该区间的 b 的排序结果(归并啊0.0..)

然后修改a就去二分区间保存答案..

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值