Icpc训练赛 + 国庆大礼包

POJ 1703 并查集

[Problem]
有A、B两个集合以及N个人,每个人所属一个集合。维护两种操作:D X Y 代表X和Y属于不同聚合;A X Y 判断XY是否属于一个集合
[Solution]
用i代表第i个人属于A集合, i + N代表第i个人属于B集合,即每个数字表示一种限制条件;数字在同一个集合表示对应的条件必须同时满足;
对于D(X,Y):Union(X, Y + N), Union(X + N, Y)
对于A(X,Y) : 判断(X, Y)与(X+ N ,Y+ N) 的所属集合
[Code]

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<map>
#include<cstring>
#include<ctime>
using namespace std;
typedef long long ll;
int f[210000];
int n, m;
int find(int x)  {
    if (f[x] == x)   return x;
    return f[x] = find(f[x]);
}
void unio(int x, int y)   {
    int xx = find(x), yy = find(y);
    f[xx] = yy;
}
bool cal(int x, int y)  {
    int xx = find(x), yy = find(y);
    return xx == yy;
}
int main()
{
   // freopen("b.in", "r", stdin);
    int T;
    scanf("%d", &T);
    while(T--)  {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n * 2; i++)  f[i] = i;
        char s[5];
        for(int i = 1; i <= m; i++)  {
            scanf("%s", s);
            int x, y;
            scanf("%d%d", &x, &y);
            if (s[0] == 'D')  {
                unio(x, y + n);
                unio(x + n, y);
            }
            else   {
                if (cal(x, y)  || cal(x + n, y + n))  printf("In the same gang.\n");
                else if (cal(x, y + n) || cal(x + n, y))  printf("In different gangs.\n");
                else  printf("Not sure yet.\n");
            }
        }
    }
    return 0;
}

POJ 2750 环形最大子序列连续和

[Problem]
给定一段环形序列,求解最大子序列连续和,确保不是原全部序列
[Solution]
对于朴素的最大子序列连续和,可以用动态规划来实现,即Dp[i] = max(Dp[i - 1], Dp[i - 1] + A[i]) 那可不可以把环形的序列转化到线性上来呢?可以把原序列复制一遍,放到原序列后面,这样对于环形的每种情况,都可以在新序列中体现出来,但注意到,新序列的一些连续子序列不一定合法(长度可能超过N),成功GG
我们考虑A[1]~A[N],如果我们单独对这个序列求解最大连续子序列和,会少考虑环形序列中跨越A[1]和A[N]的情况,在这种情况下,原序列和是固定的,选取的连续子序列和最大,但是剩余的连续和最小,因此我们只需要求解出A[1]~A[N]中最大连续子序列和、最小连续子序列和即可。
对于修改操作,通过线段树单点修改即可。

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
const int N = 110000;
typedef long long ll;
#define lson x << 1, l, mid
#define rson x << 1 | 1, mid + 1, r
#define lx x << 1
#define rx x << 1 | 1
int ql, qr, qa;
int n, m;
int a[N];
struct node {
    int lmax, lmin, rmax, rmin, sum, mmax, mmin;
} tree[N << 2];
void pushup(int x)  {
    tree[x].sum = tree[lx].sum + tree[rx].sum;
    tree[x].lmax = max(tree[lx].lmax, tree[lx].sum + tree[rx].lmax);
    tree[x].rmax = max(tree[rx].rmax, tree[rx].sum + tree[lx].rmax);
    tree[x].lmin = min(tree[lx].lmin, tree[lx].sum + tree[rx].lmin);
    tree[x].rmin = min(tree[rx].rmin, tree[rx].sum + tree[lx].rmin);
    tree[x].mmax = max(max(tree[lx].mmax, tree[rx].mmax), tree[lx].rmax + tree[rx].lmax);
    tree[x].mmin = min(min(tree[lx].mmin, tree[rx].mmin), tree[lx].rmin + tree[rx].lmin);
}
void build(int x, int l, int r)  {
    int mid = (l + r) >> 1;
    if (l == r)  {
        tree[x].sum = tree[x].lmin = tree[x].lmax = tree[x].rmin = tree[x].rmax = tree[x].mmax = tree[x].mmin = a[l];
        return ;
    }
    build(lson);
    build(rson);
    pushup(x);
}
void update(int x, int l, int r)  {
    int mid = (l + r) >> 1;
    if (l == r)  {
        tree[x].sum = tree[x].lmin = tree[x].lmax = tree[x].rmin = tree[x].rmax = tree[x].mmax = tree[x].mmin = qa;
        return;
    }
    if (ql <= mid) update(lson);  else update(rson);
    pushup(x);
}
int main(){
  //  freopen("a.in", "r", stdin);
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)  scanf("%d", a + i);
    build(1, 1, n);
    scanf("%d", &m);
    while(m--) {
        scanf("%d%d", &ql, &qa);
        update(1, 1, n);
        int ans = max(tree[1].mmax, tree[1].sum - tree[1].mmin);
        if (ans == tree[1].sum) ans -= tree[1].mmin;
        printf("%d\n", ans);
    }
    return 0;
}

POJ 2274

[Problem]
n个spaceship,每个有速度和位置,最后询问碰撞次数,并输出前10000次碰撞
[Solution]
碰撞次数就是逆序对的次数,用树状数组来搞
显然,每次碰撞都是相邻的先发生碰撞,因此我们用线段树来维护每个spaceship发生碰撞的时间,每次取出所用时间最短的碰撞,然后更新这两个点的time,注意同时也可能会影响周围的时间。

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<set>
#include<cmath>
using namespace std;
typedef long long LL;
#define lson x << 1, l, mid
#define rson x << 1 | 1, mid + 1, r
#define lx x << 1
#define rx x << 1 | 1
#define inf  1e10
#define mo 1000000
const int N = 300000;
int n ,m;
int ql;
struct node {
    int num;
    double x, v, val;
}  a[N];
struct dat {
    double minn;
    int num;
} tree[N << 2];
int c[N];
int lowbit(int x)  {  return x & -x;}
void add(int x, int val)  {
    while(x <= 100)  {
        c[x] += val;
        x += lowbit(x);
    }
}
int sum(int x)  {
    int ret = 0;
    while(x > 0)  {
        ret += c[x];
        x -= lowbit(x);
    }
    return ret;
}
bool cmp(dat x, dat y)  {
    if (x.minn == y.minn)  {
        double k1 = a[x.num].x + a[x.num].v * a[x.num].val, k2 = a[y.num].x + a[y.num].v * a[y.num].val;
        if (k1 == k2)  return x.num < y.num;
        return k1 < k2;
    }
    return x.minn < y.minn;
}
void pushup(int x)  {
    if (cmp(tree[lx], tree[rx])) tree[x] = tree[lx];
    else tree[x] = tree[rx];
}
void build(int x, int l, int r)  {
    if (l == r)  {
        tree[x].minn =a[l].val;
        tree[x].num = l;
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    pushup(x);
}
void update(int x, int l, int r)  {
    if (l == r)  {
        tree[x].minn =a[l].val;
        tree[x].num = l;
        return;
    }
    int mid = (l + r) >> 1;
    if (ql <= mid) update(lson);
    else update(rson);
    pushup(x);
}
int calc()  {
    int ans = 0;
    for(int i = 1; i <= 100; i++)  c[i] = 0;
    for(int i = 1; i <= n; i++)  {
        int x = a[i].x, v = a[i].v;
        int t = sum(100) - sum(v);
        ans = (ans + t) % mo;
        add(v, 1);
    }
    return ans;
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n ;i++) {
        scanf("%lf%lf", &a[i].x, &a[i].v);
        a[i].num = i;
    }
    for(int i = 1; i < n; i++)  {
        if (a[i].v <= a[i + 1].v)  a[i].val = inf;
               else a[i].val = (a[i + 1].x - a[i].x) / (a[i].v - a[i + 1].v);
    }
    a[n].val = inf;
    build(1, 1, n);
    printf("%d\n", calc());
    for(int i = 1; i <= 10000; i++)  {
        dat e = tree[1];
        if (e.minn >= inf)  break;
        int num = e.num;
        printf("%d %d\n", a[num].num, a[num + 1].num, a[num].val);
        if(num > 1)  {
            if (a[num - 1].v <= a[num + 1].v) a[num - 1].val = inf;
            else a[num - 1].val = abs((a[num + 1].x - a[num - 1].x)) / (a[num - 1].v - a[num + 1].v);
            ql = num - 1;
            update(1, 1, n);
        }
        node pre = a[num + 1];
        a[num + 1] = a[num];
        a[num + 1].val = inf;
        if (num + 2 <= n && a[num + 1].v > a[num + 2].v) a[num + 1].val = abs((a[num + 2].x - a[num + 1].x)) / (a[num + 1].v - a[num + 2].v);
        ql = num + 1;
        update(1, 1, n);
        a[num] = pre;
        a[num].val = inf;
        ql = num;
        update(1, 1, n);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值