Codeforces Round #216 (Div. 2) (又是线段树)

A:

先处理type1,然后type2

B:

看这个数据范围直接暴力,一个一个放进去就可以了,也可以尽可能平摊这个区间,剩余的k个分给前k个就行了,反正有解的话肯定是可行的。

C:

简单的遍历一棵树,如果某个节点下面没有坏边并且父边是坏边,则加入答案中。

#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
using namespace std;

const int maxn = 100000 + 10;

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

int head[maxn], E, pr[maxn];

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

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

bool dfs(int u, int pre, bool flag) {
    bool yes = false;
    for(int i = head[u];i != -1;i = edge[i].next) {
        int to = edge[i].to;
        if(to == pre)   continue;
        if(dfs(to, u, edge[i].k == 2))  yes = true;
    }
    if(!yes && flag)    pr[u] = 1;
    return yes || flag;
}

int main() {
    init();
    int n, u, to, k;
    scanf("%d", &n);
    for(int i = 0;i < n-1; i++) {
        scanf("%d%d%d", &u, &to, &k);
        newedge(u, to, k);
        newedge(to, u, k);
    }
    dfs(1, -1, 0);
    int ans = 0;
    for(int i = 1;i <= n; i++)  ans += pr[i];
    printf("%d\n", ans);
    for(int i = 1;i <= n; i++) if(pr[i])    printf("%d ", i); puts("");
    return 0;
}

      
      
     
     
    
    

D:

思路:可以知道所有的状态一定可以用 (i, j)表示,i前面的肯定全被杀死了,i 、j中间的也都被杀死了,j后面的都还活着,所以总的情况数是 n^2的。这样子的话我用t[i]表示剩余的人为 i ~ n至少需要几秒,对于每个i都判断下j (j > i)这个位置是否可行,表示状态 (i, j),有种特殊情况是最后全部都死了,这个的话判断什么时候只剩下两个人的时候他们的p都是大于0的就可行,用个flag标记下即可。

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
using namespace std;

const int maxn = 3000 + 10;
const int INF = 1<<30;

int p[maxn], f1[maxn], f2[maxn], t[maxn];

int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 1;i <= n; i++)
        scanf("%d", &p[i]);
    for(int i = 1;i <= n; i++)  t[i] = i == 1 ? 0 : INF;
    for(int i = n;i >= 1; i--) {
        if(f1[i+1] || p[i] == 100)  f1[i] = 1;
        if(f2[i+1] || p[i] > 0) f2[i] = 1;
    }
    int ans = 0;
    bool ok = 0;
    for(int i = 1;i <= n; i++) if(t[i] <= k) {
        if(p[i] != 100 && f2[i+1]) {
            t[i+1] = min(t[i+1], t[i]+1);
        }
        ans++;
        if(p[i] > 0) {
            if(f1[i+1]) {
                t[i+2] = min(t[i+2], t[i] + 1);
                if(i + 1 == n)  ok = 1;
            }
            else {
                ans += min(k - t[i], n - i);
                for(int j = i+1;j <= n; j++) if(f2[j] && t[i] + j - i <= k){
                    if(j == n)  ok = 1;
                    t[j+1] = min(t[j+1] , t[i] + j - i);
                    if(p[i] != 100) t[j] = min(t[j], t[i] + j - i);
                }
            }
        }
        else {
            if(f2[i+1]) t[i+1] = min(t[i+1], t[i] + 1);
        }
    }
    if(ok)  ans++;
    printf("%d\n", ans);
    return 0;
}

     
     
    
    
   
   

E:

给你一些线段,然后每次询问一些点,问这些点在多少条不同线段中。

思路:

对于每次询问的这些点,相邻两个点建一个线段,在最后一个点后加一个超远点和它建一个线段,所有的询问都这样处理,然后按右端点从小到大排序。对于原始线段也按右端点从小到大排序,每次拿出新建的线段,将原始线段中右端点值小于该线段右端点的线段插入到线段树中,然后询问新建的线段的左端点就可以了,这样子不会出现重复的,画个图想想就明白了。

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

const int maxn = 1000000 + 100;

struct PP {
    int l, r, id;
    bool operator < (const PP &a) const {
        return r < a.r;
    }
}a[maxn], b[maxn];

int mark[maxn<<2], pr[maxn];

void down(int rt) {
    if(mark[rt]) {
        mark[rt<<1] += mark[rt];
        mark[rt<<1|1] += mark[rt];
        mark[rt] = 0;
    }
}

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

int query(int rt, int l, int r, int x) {
    if(l == r && l == x) {
        return mark[rt];
    }
    down(rt);
    int mid = (l + r)/2;
    if(x <= mid)    return query(lson, x);
    else    return query(rson, x);
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0;i < n; i++)
        scanf("%d%d", &a[i].l, &a[i].r);
    int tot = 0, mx = 1000000+1;
    for(int i = 0;i < m; i++) {
        int cnt, x;
        scanf("%d", &cnt);
        scanf("%d", &x);
        for(int j = 0;j < cnt-1; j++) {
            scanf("%d", &b[tot].r);
            b[tot].l = x; b[tot].id = i;
            x = b[tot++].r;
        }
        b[tot].id = i; b[tot].l = x; b[tot++].r = mx;
    }
    sort(a, a + n);
    sort(b, b + tot);
    int cur = -1;
    for(int i = 0;i < tot; i++) {
        while(cur+1 < n && a[cur+1].r < b[i].r)
            update(1, 1, mx, a[cur+1].l, a[cur+1].r) , cur++;
        int pp = query(1, 1, mx, b[i].l);
        pr[b[i].id] += pp;
    }
    for(int i = 0;i < m; i++)   printf("%d\n", pr[i]);
    return 0;
}

     
     
    
    
   
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值