Codeforces Round #200 (Div. 1) (树上的线段树)

A:

a/b,a > b的话通过串联变回(a - b)/b,a < b的话通过并联来变

B:

仔细想想其实就是个简单的栈操作,如果加入的东西和栈顶相同,则两个消掉,否则入栈。

C:

给你一些士兵的位置,再给你一些最后要占领的位置,问这些士兵最少需要多久使得所有要被占领的位置都被走过。

思路:

首先对所有士兵位置排序,给所有要占领位置排序,可以考虑最左边的占领位置肯定是由最左边的士兵来走是最优的,对于一个确定的时间,这个士兵要保证占领了最左边的位置的情况下最多可以占领左边的多少位置是可以计算出来的,可以通过先走到最左边的位置再往右走尽可能远,或者先走向右边尽可能远但是要保证要能走回到最左边位置。第一个士兵处理好前几个位置,后面的士兵当然也是一样子的,所以二分下答案就可以了。

#include 
   
   
    
    
#include 
    
    
     
     
using namespace std;
typedef __int64 ll;

const int maxn = 100000 + 10;

ll a[maxn], b[maxn];
int n, m;

bool solve(ll step) {
    int pre = -1;
    for(int i = 0;i < n; i++) {
        ll cur = step;
        if(a[i] > b[pre+1]) {
            if(cur < a[i] - b[pre+1])  return false;
            ll pos = max(cur - (a[i] - b[pre+1])*2 , (cur - (a[i] - b[pre+1]))/2);
            pos += a[i];
            while(pre+1 < m && b[pre+1] <= a[i])    pre++;
            while(pre+1 < m && b[pre+1] <= pos) pre++;
        }
        else {
            ll pos = a[i] + cur;
            while(pre+1 < m && b[pre+1] <= pos) pre++;
        }
        if(pre == m-1)  return true;
    }
    if(pre+1 < m)   return false;
    return true;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 0;i < n; i++)   scanf("%I64d", &a[i]);
    for(int i = 0;i < m; i++)   scanf("%I64d", &b[i]);
    sort(a, a + n);
    sort(b, b + m);
    ll L = 0, R = 1LL<<50;
    ll ans = R;
    while(L <= R) {
        ll mid = (L + R)/2;
        if(solve(mid)) {
            ans = mid; R = mid - 1;
        }
        else    L = mid + 1;
    }
    printf("%I64d\n", ans);
    return 0;
}

    
    
   
   

D:

给你一棵树,树根为节点1,每次有三种操作,第一种是某一个节点注水,该节点的所有儿子也会被注水。第二种是对某一个节点清空,该节点和该节点的所有祖先都会被清空,也就是说有水的都会被清空。第三种操作是询问某一个节点有没有水。


思路:

首先可以先序遍历下树处理出所有节点的时间戳,所有节点的所有儿子节点包括自己代表了一个区间,ld[ u ]表示区间左值,rd[ u ]表示区间右值。可以开两个线段树,一个线段树处理注水操作,另一个线段树处理清空操作,线段树都是维护区间最值的。如果第i个操作是对节点u进行注水操作,对于区间 ( ld[u] , rd[u] )插入 i 来更新, 如果是清空操作,用 i 更新ld[u]的值,询问u的时候,询问下ld[u]注水操作的值和清空操作的值,如果前者大,则有水,否则无水。

我觉得这个应该是很基础的,不过对于所有祖先这样子操作还是挺神奇的。。。下次学下树链剖分~

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
using namespace std;
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r

const int maxn = 500000 + 10;

struct Edge {
    int to, next;
}edge[maxn<<1];

int head[maxn], E, Time, node[2][maxn<<2], mark[2][maxn<<2], ld[maxn], rd[maxn];

void init() {
    memset(head, -1, sizeof(head));
    E = Time = 0;
}

void newedge(int u, int to) {
    edge[E].to = to;
    edge[E].next = head[u];
    head[u] = E++;
}

void dfs(int u) {
    ld[u] = ++Time;
    for(int i = head[u];i != -1;i = edge[i].next) {
        int to = edge[i].to;
        if(!ld[to])
            dfs(to);
    }
    rd[u] = Time;
}

void down(int rt, int fg) {
    if(mark[fg][rt]) {
        mark[fg][rt<<1] = max(mark[fg][rt<<1], mark[fg][rt]);
        mark[fg][rt<<1|1] = max(mark[fg][rt<<1|1], mark[fg][rt]);
        node[fg][rt<<1] = max(mark[fg][rt<<1], node[fg][rt<<1]);
        node[fg][rt<<1|1] = max(mark[fg][rt<<1|1], node[fg][rt<<1|1]);
        mark[fg][rt] = 0;
    }
}

void up(int rt, int fg) {
    node[fg][rt] = max(node[fg][rt<<1], node[fg][rt<<1|1]);
}

void update(int rt, int l, int r, int L, int R, int v, int fg) {
    if(L <= l && R >= r) {
        mark[fg][rt] = max(mark[fg][rt], v);
        node[fg][rt] = max(node[fg][rt], mark[fg][rt]);
        return ;
    }
    down(rt, fg);
    int mid = (l + r)/2;
    if(L <= mid)    update(lson, L, R, v, fg);
    if(R > mid) update(rson, L, R, v, fg);
    up(rt, fg);
}

int query(int rt, int l, int r, int L, int R, int fg) {
    if(L <= l && R >= r) {
        return node[fg][rt];
    }
    down(rt, fg);
    int mid = (l + r)/2;
    int ret = 0;
    if(L <= mid)    ret = max(ret, query(lson, L, R, fg));
    if(R > mid) ret = max(ret, query(rson, L, R, fg));
    up(rt, fg);
    return ret;
}

int main() {
    init();
    int n, to, u;
    scanf("%d", &n);
    for(int i = 1;i < n; i++) {
        scanf("%d%d", &u, &to);
        newedge(u, to);
        newedge(to, u);
    }
    dfs(1);
    int m, c, v;
    scanf("%d", &m);
    for(int i = 1;i <= m ;i++) {
        scanf("%d%d", &c, &v);
        if(c == 1)
            update(1, 1, n, ld[v], rd[v], i, 0);
        else if(c == 2)
            update(1, 1, n, ld[v], ld[v], i, 1);
        else {
            int x = query(1, 1, n, ld[v], ld[v], 0);
            int y = query(1, 1, n, ld[v], rd[v], 1);
            if(x > y)   puts("1");
            else    puts("0");
        }
    }
    return 0;
}

     
     
    
    
   
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值