Test链接:https://cn.vjudge.net/contest/231849
A:区间求差
给一组区间集合A和区间集合B,求A-B的长度
思路:
首先进行离散化,对每个点进行标号处理,对于A中的每个区间,用树状数组把离散化后的A中的每个区域标记。
对于B的每个区间,用另一个树状数组标记。
这里对点i标记,等价于标记线段[i-1, i](i为离散化后的标号)
之后对每个点查询是否在A中标记,在B中未标记,那就是A-B的子区间,加上这个点表示的线段长度即可。
时间复杂度O(nlog(n))
#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
const int maxn = 1e6 + ;
int a[maxn], b[maxn];
int tree_a[maxn], tree_b[maxn];
bool vis[maxn];
int tot2;
int sum(int x, int a[])
{
int ret = ;
while(x <= tot2)
{
ret += a[x];
x += lowbit(x);
}
return ret;
}
//向前修改[0, x]整个区间加上d
void add(int x, int d, int a[])
{
while(x > )
{
a[x] += d;
x -= lowbit(x);
}
} int main()
{
int n, m;
scanf("%d%d", &n, &m);
int tot = * n + * m, tot1 = * n;
for(int i = ; i <= tot1; i++)scanf("%d", &a[i]);
for(int i = tot1 + ; i <= tot; i++)scanf("%d", &a[i]);
memcpy(b, a, sizeof(a));
sort(b + , b + tot + );
tot2 = unique(b + , b + tot + ) - (b + );
//for(int i = 1; i <= tot2)
for(int i = ; i <= tot1; i += )
{
int x = lower_bound(b + , b + tot2 + , a[i]) - b;
int y = lower_bound(b + , b + tot2 + , a[i + ]) - b;
add(x, -, tree_a);
add(y, , tree_a);
}
for(int i = tot1 + ; i <= tot; i += )
{
int x = lower_bound(b + , b + tot2 + , a[i]) - b;
int y = lower_bound(b + , b + tot2 + , a[i + ]) - b;
add(x, -, tree_b);
add(y, , tree_b);
}
ll ans = ;
for(int i = ; i <= tot2; i++)
{
if(sum(i, tree_a) > && sum(i, tree_b) == )
{
//cout<<i<<endl;
ans += b[i] - b[i - ];
}
}
cout<<ans<<endl;
return ;
}
B:股票价格
动态模拟下列操作
P x y x时刻股票价格为y
R x 清除x之前的所有股票价格信息
Q 输出目前已知的最大 最小 最近股票价格(不包括删除的股票)
保证P x y中的时刻x为升序
思路:
用queue动态保存x和y,每次入队更新最大值,最小值,最新值,在Q操作时直接输出即可
对于删除操作,由于给的x是升序,所以可以直接利用队列的pop操作。
但是,删除之后,要更新最大值最小值,所以用set和map存储目前在队列中的x值,以及出现的次数,每加入和删除一个数字的时候更新set和map,删除操作完成后,更新最大值最小值(此时要保证队列中还有数字,如果没有,要将这两个数重新初始化)
#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
struct node
{
int a, b;
node(){}
node(int a, int b):a(a), b(b){}
};
set<int>s;
map<int, int>Map;
queue<node>q;
int main()
{
int n, x, y;
cin >> n;
int Max = -, Min = 1e9+, last;
char a[];
while(n--)
{
scanf("%s", a);
if(a[] == 'P')
{
scanf("%d%d", &x, &y);
q.push(node(x, y));
Max = max(Max, y);
Min = min(Min, y);
last = y;
Map[y]++;
s.insert(y);
}
else if(a[] == 'Q')
{
printf("%d %d %d\n", Max, Min, last);
}
else if(a[] == 'R')
{
scanf("%d", &x);
while(q.front().a <= x)
{
y = q.front().b;
q.pop();
Map[y]--;
if(Map[y] == )s.erase(s.find(y));
}
if(s.size())
{
Min = *s.begin();
set<int>::iterator it = s.end();
it--;
Max = *it;
}
else
{
Max = -, Min = 1e9 + ;
}
}
}
return ;
}
C:穿越禁区
给出一个矩形区域,以及多个圆的信息(圆心在矩形内),判断能否从矩形左侧到达矩形右侧,不和圆进行接触。
思路:
如果不能到达右侧,说明中间被一群圆挡住了,过不去。
此时肯定有圆和矩形底端接触,也有圆和矩形顶部接触。(如果没有,就可以沿着矩形边缘到达右侧,所以一定存在)
如果中间被一群圆档住了过不去,等价于和底部接触的圆通过中间的圆连接,可以和顶部接触的圆接触。
在输入时记录与底部接触的圆的下标和顶部接触的圆的下标。
之后对每两个圆进行连接,如果两圆相交,这两个圆就连通,用并查集加入连通分量。
最后判断是否存在一个底部的圆和顶部的某个圆在同一个连通分量内,如果存在,就说明不可以通过,否则就可以通过。
在判断圆相交的时候注意long long,这里用平方判断的,需要long long
#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
struct node
{
ll x, y, r;
}a[];
ll low[], high[];
ll p[];
ll Find(ll x)
{
return p[x] == x ? x : p[x] = Find(p[x]);
}
bool judge(ll i, ll j)
{
ll t = (a[i].x - a[j].x) * (a[i].x - a[j].x) + (a[i].y - a[j].y) * (a[i].y - a[j].y);
ll c = a[i].r + a[j].r;
return c * c >= t;
}
int main()
{
ll T, w, h, n;
cin >> T;
while(T--)
{
cin >> w >> h >> n;
ll tot1 = , tot2 = , flag = ;
for(int i = ; i <= n; i++)p[i] = i;
for(int i = ; i < n; i++)
{
scanf("%lld%lld%lld", &a[i].x, &a[i].y, &a[i].r);
if(a[i].y <= a[i].r)
{
low[tot1++] = i;
}
if(h - a[i].y <= a[i].r)
{
high[tot2++] = i;
}
if(tot1 && tot2 && low[tot1 - ] == high[tot2 - ])
{
flag = ;
}
}
if(!flag)
{
for(int i = ; i < n; i++)
{
for(int j = i + ; j < n; j++)
{
if(judge(i, j))
{
//cout<<i<<" "<<j<<endl;
ll x = Find(i), y = Find(j);
p[x] = y;
}
}
}
for(int i = ; i < tot1; i++)
{
for(int j = ; j < tot2; j++)
{
if(Find(low[i]) == Find(high[j]))
{
flag = ;
break;
}
}
if(flag)break;
}
}
if(flag)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return ;
}