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;
}