SDNU_ACM_ICPC_2020_Winter_Practice_3rd
B - lzwの作业 CodeForces - 1242B
Ujan has a lot of useless stuff in his drawers, a considerable part of which are his math notebooks: it is time to sort them out. This time he found an old dusty graph theory notebook with a description of a graph.
It is an undirected weighted graph on n vertices. It is a complete graph: each pair of vertices is connected by an edge. The weight of each edge is either 0 or 1; exactly m edges have weight 1, and all others have weight 0.
Since Ujan doesn’t really want to organize his notes, he decided to find the weight of the minimum spanning tree of the graph. (The weight of a spanning tree is the sum of all its edges.) Can you find the answer for Ujan so he stops procrastinating?
题解
大意为,n个点的完全图,有m条边的边权是1,其余为0,问最小生成树的值是多少
由于是完全图,则从任意一个点开始遍历
用权重为0的边连接的点判为一个联通块,而每个联通块之间,则用权重为1的边相连.
于是大力搜索找联通块个数即可.
#pragma comment(linker, "/STACK:102400000,102400000")
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
const double pi = acos(-1.0);
const double eps = 1e-12;
typedef long long ll;
const ll INF = LLONG_MAX;
const ll ENF = LLONG_MIN;
const int MAXN = 1e5 + 10;
const int inf = __INT_MAX__;
const int enf = INT_MIN;
set<int> e[MAXN];
set<int> s;
void dfs(int u)
{
set<int> st;
for (auto v : s)
if (e[u].find(v) == e[u].end())
st.insert(v);
for (auto v : st)
s.erase(v);
for (auto v : st)
dfs(v);
}
int main()
{
int n, m, ans = 0;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
e[u].insert(v);
e[v].insert(u);
}
for (int i = 1; i <= n; i++)
s.insert(i);
while (!s.empty()) {
int u = *s.begin();
s.erase(u);
dfs(u);
ans++;
}
cout << ans - 1 << endl;
}
C - lzwの高尔夫 UVA - 12879
Do you like golf? I hate it. I hate golf so much that Idecided to build the ultimate golf robot, a robot thatwill never miss a shot. I simply place it over the ball,choose the right direction and distance and, awlessly,it will strike the ball across the air and into the hole.Golf will never be played again.Unfortunately, it doesn’t work as planned. So, hereI am, standing in the green and preparing my rststrike when I realize that the distance-selector knobbuilt-in doesn’t have all the distance options! Not ev-erything is lost, as I have 2 shots.Given my current robot, how many holes will I beable to complete in 2 strokes or less? The ball must bealways on the right line between the tee and the hole.It isn’t allowed to overstep it and come back.
题解
很迷的题目,UVALive一样的题目,不知道为什么过不去
\sqrt{}
了新技能,用位运算来代替加法
我们认为,数字
a
a
a在
0
,
1
0,1
0,1子串的意义为第
a
a
a位为1
那么,
a
+
b
a+b
a+b意味着第
a
+
b
a+b
a+b位为1
即可理解为将第
a
a
a位上的1左移
b
b
b位
那么,我们将结果存在一个
0
,
1
0,1
0,1子串里,将加数存在另一个里
判断即可
#pragma comment(linker, "/STACK:102400000,102400000")
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
const double pi = acos(-1.0);
const double eps = 1e-12;
typedef long long ll;
const ll INF = LLONG_MAX;
const ll ENF = LLONG_MIN;
const int MAXN = 2e5 + 10;
const int inf = __INT_MAX__;
const int enf = INT_MIN;
int a[MAXN];
bitset<MAXN + 1> bt2, bt1;
int main()
{
int n, m, p;
while (~scanf("%d", &n)) {
bt1.reset();
bt2.reset();
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
bt1[a[i]] = 1;
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%d", &p);
bt2[p] = 1;
}
for (int i = 0; i <= n; i++) {
bt2 ^= (bt2 & (bt1 << a[i]));
}
printf("%d\n", m - (int)bt2.count());
}
return 0;
}
L - lzwの花园 HYSBZ - 1935
很久很久以前,在遥远的大陆上有一个美丽的国家。统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草。有一天国王漫步在花园里,若有所思,他问一个园丁道: “最近我在思索一个问题,如果我们把花坛摆成六个六角形,那么……” “那么本质上它是一个深度优先搜索,陛下”,园丁深深地向国王鞠了一躬。 “嗯……我听说有一种怪物叫九头蛇,它非常贪吃苹果树……” “是的,显然这是一道经典的动态规划题,早在N元4002年我们就已经发现了其中的奥秘了,陛下”。 “该死的,你究竟是什么来头?” “陛下息怒,干我们的这行经常莫名其妙地被问到和OI有关的题目,我也是为了预防万一啊!” 王者的尊严受到了伤害,这是不可容忍的。看来一般的难题是难不倒这位园丁的,国王最后打算用车轮战来消耗他的实力: “年轻人,在我的花园里的每一棵树可以用一个整数坐标来表示,一会儿,我的骑士们会来轮番询问你某一个矩阵内有多少树,如果你不能立即答对,你就准备走人吧!”说完,国王气呼呼地先走了。 这下轮到园丁傻眼了,他没有准备过这样的问题。所幸的是,作为“全国园丁保护联盟”的会长——你,可以成为他的最后一根救命稻草。
题解
二维前缀和问题,询问一个矩形内点的个数
由于坐标过大,没法使用数组,必须离散化,故不能简单地使用二位前缀和求解
可供选择的为树套树,k-d树,cdq
k-d树,永远被卡的那个,最后一个数据点要跑
2.2
s
2.2s
2.2s,故
p
a
s
pas
pas
则使用常数小的cdq分治
既然是求解矩形,那么我们直接将询问的坐标的前缀和,以及所给坐标的前缀和都求出来,然后直接
a
−
b
−
c
+
d
a-b-c+d
a−b−c+d求解就完事了
具体看代码注释
#pragma comment(linker, "/STACK:102400000,102400000")
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
const double pi = acos(-1.0);
const double eps = 1e-12;
typedef long long ll;
const ll INF = LLONG_MAX;
const ll ENF = LLONG_MIN;
const int MAXN = 3e6 + 10;
const int inf = __INT_MAX__;
const int enf = INT_MIN;
struct point {
int x, y, opt, id;
bool operator<(point a)const
{
return (x == a.x ? (y == a.y ? opt : y < a.y) : x < a.x);
}
} p[MAXN], tmp[MAXN];
int ans[MAXN], p_tot, q_tot;
void cdq(int l, int r)
{
if (l == r)
return;
int mid = (l + r) >> 1;
cdq(l, mid);
cdq(mid + 1, r);
int ll = l, lr = mid + 1, tot = 0, it = l;
while (ll <= mid && lr <= r) //求前缀和,统计答案
if (p[ll].y <= p[lr].y) {
tot += p[ll].opt, tmp[it] = p[ll];
it++, ll++;
} else {
ans[p[lr].id] += tot, tmp[it] = p[lr];
lr++, it++;
}
while (ll <= mid) {
tmp[it] = p[ll];
it++, ll++;
}
while (lr <= r) {
ans[p[lr].id] += tot, tmp[it] = p[lr];
it++, lr++;
}
for (int i = l; i <= r; i++) //tmp的作用是将该区间内的点按照y从小到大排序
p[i] = tmp[i];
}
int main()
{
int n, m, a, b, c, d;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
scanf("%d%d", &p[i].x, &p[i].y);
p[i].opt = 1;
}
p_tot = n;
while (m--) {
scanf("%d%d%d%d", &a, &b, &c, &d); //拆点,将矩形拆成四个前缀和运算
p[++p_tot] = point{ c, d, 0, ++q_tot };
p[++p_tot] = point{ a - 1, d, 0, ++q_tot };
p[++p_tot] = point{ c, b - 1, 0, ++q_tot };
p[++p_tot] = point{ a - 1, b - 1, 0, ++q_tot };
}
sort(p + 1, p + p_tot + 1);
cdq(1, p_tot);
for (int i = 1; i + 3 <= q_tot; i += 4) {
int sum = ans[i] - ans[i + 1] - ans[i + 2] + ans[i + 3];
printf("%d\n", sum);
}
return 0;
}