liaoliao的四连做..

写在前面的..

昨天来了一波非常激情的liaoliao四连做..

然后我很幸运的脑(bao)短(ling)路(le)..

今天又很开森的bak回这四道挺好的题啊..


t1 1190: [HNOI2007]梦幻岛宝珠

先把每个数拆成 a×2b ,按照位来dp..

fi,j 表示在 j×2i 以内的最高价值..

然后就背包搞一下就好了,注意从 i 递推到i+1的状况..

如果 w 的第i位是 0 ,那么对于fi,k (k mod 2==1),就要递推到 fi+1,k+12 ,因为已经没有可能在后面再涉及到第 i 位的状态了..

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int f[32][1010];
struct node {
    int a, b, val;
}list[110];
int n, W, len, w[32];
int lowbit ( int x ){ return x & (-x); }
bool cmp ( node x, node y ){ return x.b < y.b; }
int _max ( int x, int y ){ return x > y ? x : y; }
int main (){
    int i, j, k;
    while ( scanf ( "%d%d", &n, &W ) && n >= 0 && W >= 0 ){
        len = -1;
        while (W){
            w[++len] = W%2;
            W /= 2;
        }
        for ( i = 1; i <= n; i ++ ){
            int x;
            scanf ( "%d%d", &x, &list[i].val );
            list[i].b = lowbit (x);
            list[i].a = x/list[i].b;
        }
        memset ( f, 0, sizeof (f) );
        sort ( list+1, list+n+1, cmp );
        int maxl = 0;
        j = 1;
        for ( i = 0; i <= len; i ++ ){
            while ( list[j].b == (1<<i) && j <= n ){
                for ( k = maxl; k >= 0; k -- ) f[i][k+list[j].a] = _max ( f[i][k+list[j].a], f[i][k]+list[j].val );
                maxl += list[j].a;
                j ++;
            }
            if ( w[i] == 0 ){
                for ( k = 0; k <= maxl; k ++ ){
                    if ( k & 1 ) f[i+1][(k+1)/2] = _max ( f[i+1][(k+1)/2], f[i][k] );
                    else f[i+1][k/2] = _max ( f[i+1][k/2], f[i][k] );
                }
                if ( maxl & 1 ) maxl = (maxl+1) >> 1;
                else maxl = maxl >> 1;
            }
            else {
                for ( k = 0; k <= maxl; k ++ ) f[i+1][k/2] = _max ( f[i+1][k/2], f[i][k] );
                maxl = maxl >> 1;
            }
        }
        printf ( "%d\n", _max ( f[len][0], f[len][1] ) );
    }
    return 0;
}

t2 4310: 跳蚤

搞个后缀数组..

二分答案串,然后在原串中匹配,匹配到如果某个位置字典序比答案串该位置的要大,那么证明在这段区间必须要砍一刀..

那么就转化成一个很经典的贪心问题了:n条线段,找最少的点使得每条线段至少有一个点..

先去掉包含的线段,再把剩余线段按照左端点排序,每次选择第一条线段的右端点,直到遇到某个左端点在该右端点右边..

#include <cstdio> 
#include <cstring> 
#include <cstdlib> 
#include <algorithm> 
using namespace std; 
const int Maxn = 100010; 
int sa[Maxn], rank[Maxn], y[Maxn], wr[Maxn], hei[Maxn]; 
int Rsort[Maxn]; 
char s[Maxn]; int n; 
int K, o[Maxn], rig[Maxn]; 
int _min ( int x, int y ){ return x < y ? x : y; } 
bool cmp ( int k1, int k2, int ln ){ return wr[k1] == wr[k2] && wr[k1+ln] == wr[k2+ln]; } 
void get_sa ( int m ){ 
    int i, j, k; 
    memset ( Rsort, 0, sizeof (Rsort) ); 
    for ( i = 1; i <= n; i ++ ) Rsort[rank[i]] ++; 
    for ( i = 1; i <= m; i ++ ) Rsort[i] += Rsort[i-1]; 
    for ( i = n; i >= 1; i -- ) sa[Rsort[rank[i]]--] = i; 

    int p = 1, ln = 1; 
    while ( p < n ){ 
        k = 0; 
        for ( i = n-ln+1; i <= n; i ++ ) y[++k] = i; 
        for ( i = 1; i <= n; i ++ ){ 
            if ( sa[i]-ln > 0 ) y[++k] = sa[i]-ln; 
        } 
        for ( i = 1; i <= n; i ++ ) wr[i] = rank[y[i]]; 

        memset ( Rsort, 0, sizeof (Rsort) ); 
        for ( i = 1; i <= n; i ++ ) Rsort[wr[i]] ++; 
        for ( i = 1; i <= m; i ++ ) Rsort[i] += Rsort[i-1]; 
        for ( i = n; i >= 1; i -- ) sa[Rsort[wr[i]]--] = y[i]; 

        memcpy ( wr, rank, sizeof (wr) ); 
        p = 1; rank[sa[1]] = 1; 
        for ( i = 2; i <= n; i ++ ){ 
            if ( !cmp ( sa[i], sa[i-1], ln ) ) p ++; 
            rank[sa[i]] = p; 
        } 
        m = p; ln *= 2; 
    } 
} 
void get_hei (){ 
    int i, j, k; 
    k = 0; 
    for ( i = 1; i <= n; i ++ ){ 
        j = sa[rank[i]-1]; 
        if (k) k --; 
        while ( s[i+k] == s[j+k] ) k ++; 
        hei[rank[i]] = k; 
    } 
} 
int bit[Maxn], f[Maxn][20]; 
int qu ( int x, int y ){ 
    if ( x == y ) return 0x7fffffff; 
    if ( x > y ) swap ( x, y ); 
    int le = y-x; 
    return _min ( f[x][bit[le]], f[y-(1<<bit[le])][bit[le]] ); 
} 
struct que { 
    int x, ri; 
}list[Maxn]; int tp; 
int main (){ 
    int i, j, k; 
    scanf ( "%d", &K ); 
    scanf ( "%s", s+1 ); 
    n = strlen (s+1); 
    for ( i = 1; i <= n; i ++ ) rank[i] = s[i]; 
    get_sa (190); 
    get_hei (); 
    int l = 1, r, ret; 
    for ( i = 1; i <= n; i ++ ){ 
        o[i] = o[i-1]+n-sa[i]+1-hei[i]; 
    } 
    r = o[n]; 
    bit[1] = 0; 
    for ( i = 2; i <= n; i ++ ){ 
        if ( (1<<(bit[i-1]+1)) <= i ) bit[i] = bit[i-1]+1; 
        else bit[i] = bit[i-1]; 
    } 
    for ( i = n; i >= 1; i -- ){ 
        f[i][0] = hei[i+1]; 
        for ( j = 1; j <= 17 && i+(1<<(j-1)) <= n; j ++ ) f[i][j] = _min ( f[i][j-1], f[i+(1<<(j-1))][j-1] ); 
    } 
    while ( l <= r ){ 
        int mid = ( l + r ) >> 1; 
        int pos = lower_bound ( o+1, o+n+1, mid ) - o; 
        int len = hei[pos]+mid-o[pos-1]; 
        bool bk = true; 
        tp = 0; 
        for ( i = 1; i <= n; i ++ ){ 
            rig[i] = -1; 
            int re = _min ( qu ( rank[i], pos ), len ); 
            if ( re == len ) rig[i] = i+re; 
            else if ( s[i+re] > s[sa[pos]+re] ) rig[i] = i+re; 
            if ( rig[i] <= i && rig[i] != -1 ){ bk = false; break; } 
            if ( rig[i] != -1 ){ 
                while ( tp > 0 && list[tp].ri > rig[i] ){ 
                    rig[list[tp].x] = -1; 
                    tp --; 
                } 
                tp ++; 
                list[tp].x = i; list[tp].ri = rig[i]; 
            } 
        } 
        if ( bk == false ){ l = mid+1; continue; } 
        int sum = 0, last = 0; 
        for ( i = 1; i <= tp; i ++ ){ 
            if ( last < list[i].x ){ 
                last = list[i].ri-1; 
                sum ++; 
            } 
        } 
        if ( last == n ) sum --; 
        if ( sum <= K-1 ){ ret = mid; r = mid-1; } 
        else l = mid+1; 
    } 
    int pos = lower_bound ( o+1, o+n+1, ret ) - o; 
    int len = hei[pos]+ret-o[pos-1]; 
    for ( i = 1; i <= len; i ++ ) printf ( "%c", s[sa[pos]+i-1] ); 
    printf ( "\n" ); 
    return 0; 
}

t3 3333: 排队计划

好好想想,无论怎么变,对于每个点改变的只有这个点右边比它小的点的个数..

那么就套个线段树搞搞就好了..

mdzz.. 差点就a了..


#include <cstdio> 
#include <cstring> 
#include <cstdlib> 
#include <algorithm> 
#define LL long long 
using namespace std; 
const LL Maxn = 500010; 
LL a[Maxn], b[Maxn], bl; 
LL n, m, ans, pos, poso; 
LL seg[Maxn*4], num[Maxn*4], rig[Maxn]; 
void change ( LL now, LL L, LL R, LL x ){ 
    if ( L == R ){ seg[now] ++; return; } 
    LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1; 
    if ( x <= mid ) change ( lc, L, mid, x ); 
    else change ( rc, mid+1, R, x ); 
    seg[now] = seg[lc]+seg[rc]; 
} 
LL query ( LL now, LL L, LL R, LL l, LL r ){ 
    if ( l > r ) return 0; 
    if ( L == l && R == r ) return seg[now]; 
    LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1; 
    if ( r <= mid ) return query ( lc, L, mid, l, r ); 
    else if ( l > mid ) return query ( rc, mid+1, R, l, r ); 
    else return query ( lc, L, mid, l, mid ) + query ( rc, mid+1, R, mid+1, r ); 
} 
void bulid_tree ( LL now, LL L, LL R ){ 
    if ( L < R ){ 
        LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1; 
        bulid_tree ( lc, L, mid ); 
        bulid_tree ( rc, mid+1, R ); 
        if ( seg[lc] < seg[rc] ){ num[now] = num[lc]; seg[now] = seg[lc]; } 
        else { num[now] = num[rc]; seg[now] = seg[rc]; } 
    } 
    else { num[now] = L; seg[now] = lower_bound ( b+1, b+bl+1, a[L] ) - b; } 
} 
void ch ( LL now, LL L, LL R, LL x ){ 
    if ( L == R ){ seg[now] = 0x7fffffff; return; } 
    LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1; 
    if ( x <= mid ) ch ( lc, L, mid, x ); 
    else ch ( rc, mid+1, R, x ); 
    if ( seg[lc] < seg[rc] ){ num[now] = num[lc]; seg[now] = seg[lc]; } 
    else { num[now] = num[rc]; seg[now] = seg[rc]; } 
} 
void qu ( LL now, LL L, LL R, LL l, LL r ){ 
    if ( L == l && R == r ){ 
        if ( seg[now] < poso ){ 
            poso = seg[now]; 
            pos = num[now]; 
        } 
        return; 
    } 
    LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1; 
    if ( r <= mid ) qu ( lc, L, mid, l, r ); 
    else if ( l > mid ) qu ( rc, mid+1, R, l, r ); 
    else qu ( lc, L, mid, l, mid ), qu ( rc, mid+1, R, mid+1, r ); 
} 
int main (){ 
    LL i, j, k; 
    scanf ( "%lld%lld", &n, &m ); 
    for ( i = 1; i <= n; i ++ ) scanf ( "%lld", &a[i] ), b[i] = a[i]; 
    sort ( b+1, b+n+1 ); 
    bl = unique ( b+1, b+n+1 ) - (b+1); 
    ans = 0; 
    for ( i = n; i >= 1; i -- ){ 
        LL x = lower_bound ( b+1, b+bl+1, a[i] ) - b; 
        rig[i] = query ( 1, 1, bl, 1, x-1 ); 
        ans += rig[i]; 
        change ( 1, 1, bl, x ); 
    } 
    printf ( "%lld\n", ans ); 
    bulid_tree ( 1, 1, n ); 
    for ( i = 1; i <= m; i ++ ){ 
        LL o; 
        scanf ( "%lld", &o ); 
        LL x = lower_bound ( b+1, b+bl+1, a[o] ) - b; 
        while ( poso = 0x7fffffff, qu ( 1, 1, n, o, n ), poso <= x ){ 
            ans -= rig[pos]; 
            ch ( 1, 1, n, pos ); 
        } 
        printf ( "%lld\n", ans ); 
    } 
    return 0; 
}

t4 4455: [Zjoi2016]小星星

容斥原理啊..

一个很高深的原理..

先把原问题转成映射的问题..

然后 fi,j 表示把 i 映射到j的方案数..

最后答案就等于至少0个禁止选的-至少1个禁止选的+至少2个禁止选的..

jiry_2的blog

#include <cstdio> 
#include <cstring> 
#include <cstdlib> 
#include <algorithm> 
#define LL long long 
using namespace std; 
const int Maxn = 20; 
LL f[Maxn][Maxn]; 
struct node { 
    int y, next; 
}a[Maxn*Maxn*2], af[Maxn*2]; int first[Maxn], firstf[Maxn], len, lenf; 
int n, m; 
void ins ( int x, int y ){ 
    len ++; 
    a[len].y = y; 
    a[len].next = first[x]; first[x] = len; 
} 
void insf ( int x, int y ){ 
    lenf ++; 
    af[lenf].y = y; 
    af[lenf].next = firstf[x]; firstf[x] = lenf; 
} 
LL ans; 
bool v[Maxn]; 
void calc ( int x, int fa ){ 
    int i, j; 
    for ( int k = firstf[x]; k; k = af[k].next ){ 
        int y = af[k].y; 
        if ( y == fa ) continue; 
        calc ( y, x ); 
    } 
    for ( i = 1; i <= n; i ++ ){ 
        if ( v[i] == false ) continue; 
        f[x][i] = 1; 
        for ( int k = firstf[x]; k; k = af[k].next ){ 
            int y = af[k].y; 
            if ( y == fa ) continue; 
            LL ret = 0; 
            for ( int ki = first[i]; ki; ki = a[ki].next ){ 
                int yy = a[ki].y; 
                if ( v[yy] == false ) continue; 
                ret += f[y][yy]; 
            } 
            f[x][i] *= ret; 
        } 
    } 
} 
void check (){ 
    memset ( f, 0, sizeof (f) ); 
    calc ( 1, 0 ); 
    LL ret = 0; int sum = 0; 
    for ( int i = 1; i <= n; i ++ ){ 
        if ( v[i] == true ){ ret += f[1][i]; sum ++; } 
    } 
    //printf ( "%d %d\n", sum, ret ); 
    if ( (n-sum) % 2 == 0 ) ans += ret; 
    else ans -= ret; 
} 
void dfs ( int x ){ 
    if ( x == n+1 ){ check (); return; } 
    v[x] = false; 
    dfs (x+1); 
    v[x] = true; 
    dfs (x+1); 
} 
int main (){ 
    int i, j, k; 
    scanf ( "%d%d", &n, &m ); 
    for ( i = 1; i <= m; i ++ ){ 
        int x, y; 
        scanf ( "%d%d", &x, &y ); 
        ins ( x, y ); ins ( y, x ); 
    } 
    for ( i = 1; i < n; i ++ ){ 
        int x, y; 
        scanf ( "%d%d", &x, &y ); 
        insf ( x, y ); insf ( y, x ); 
    } 
    dfs (1); 
    printf ( "%lld\n", ans ); 
    return 0; 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值