上一周写了作业,这一周作业很多就提前写了
所以这周训练的时间会比较多一些
火力全开,把kuangbin两个专题,计算机和基础和凸包刷完
周一 4.26 (计算几何基础)
poj 2653(判断线段与线段相交)
这道题很显然的做法就是暴力,我也想到了,但是看到n有1e5以为会超时
实际上题目说了top stick不会超过1000,我对这句话没什么反应
应该要反映出来很多木块都是被排除掉的
所以写两个for循环,看起来是n方,但是第二层循环很多木条都是很早就退出循环了,因为top stick是很少的
判断线段相交,就是端点的向量叉乘就好
#include<cstdio>
#include<vector>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
const double eps = 1e-8;
struct point
{
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
};
struct line
{
point a, b;
line(point a = 0, point b = 0) : a(a), b(b) {}
}L[N];
int n;
vector<int> ans;
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }
bool is_intersect(line i, line j)
{
point a = i.a, b = i.b, c = j.a, d = j.b;
double t1 = ((a - c) ^ (b - c)) * ((a - d) ^ (b - d));
double t2 = ((c - a) ^ (d - a)) * ((c - b) ^ (d - b));
return t1 < 0 && t2 < 0; //不写等号就是不包括端点相交。写了等号包括端点相交
}
int main()
{
while(scanf("%d", &n) && n)
{
ans.clear();
_for(i, 1, n) scanf("%lf%lf%lf%lf", &L[i].a.x, &L[i].a.y, &L[i].b.x, &L[i].b.y);
_for(i, 1, n)
{
bool ok = 1;
_for(j, i + 1, n)
if(is_intersect(L[i], L[j]))
{
ok = 0;
break;
}
if(ok) ans.push_back(i);
}
printf("Top sticks: %d", ans[0]);
REP(i, 1, ans.size()) printf(", %d", ans[i]);
puts(".");
}
return 0;
}
poj 1066(思维 + 判断线段相交)
这道题好秀啊
我自己思考的时候是觉得每个房间看作一个点,墙看作一条长度为1的边,建图bfs一遍
但是不会建图,不知这么弄。要枚举每个房间这件事情很困难
充分思考后还是不知道,就看了题解
非常秀
这道题要从宏观考虑,不要从微观考虑
考虑从墙边上的一个点要到达终点
如果中间存在一个墙分隔了这两个点,那么无论如何就一定要穿过这个墙,而且只穿过一次
显然不会返回再穿过一次
所以中间有多少个墙,就至少要穿多少次
那么这么算有多少个这样的墙呢
我们可以发现这样的墙一定和起点和终点的连线相交
还发现如果墙不和连线相交,那么我们可以沿着连线直接过去,也就是说这个墙是不需要穿过的
那么答案就很明了了,看有多少个和连线相交的墙,这个墙数再加上从边界进来的一次就是答案
那么枚举每个起点,找最小值就是答案了
那么起点怎么找?
题目说的是从线段的中点
画画图可以发现,一个顶点是可以代替其两侧的终点的
我们可以画画终点的位置,发现它可以代表其中一个中点的情况,而此时另一个中点一定要多穿一个墙,舍弃掉
所以我们枚举顶点作为起点就行,因为它可以代表所有中点为起点的情况
这里还有一个坑,就是n为0的时候
我在训练赛的时候也遇到类似的坑,就是真正合法的东西为0,这时候要特判
这是一个典型的巨坑,以后做题要意识到
#include<cstdio>
#include<algorithm>
#include<vector>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 50;
const double eps = 1e-8;
struct point
{
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
};
vector<point> node;
struct line
{
point a, b;
}L[N];
int n;
double x, y;
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }
bool intersect(point a, point b, point c, point d)
{
double t1 = ((a - c) ^ (b - c)) * ((a - d) ^ (b - d));
double t2 = ((c - a) ^ (d - a)) * ((c - b) ^ (d - b));
return t1 < 0 && t2 < 0; //不严格相交
}
int main()
{
scanf("%d", &n);
if(n == 0) return puts("Number of doors = 1") && 0;
_for(i, 1, n)
{
scanf("%lf%lf%lf%lf", &L[i].a.x, &L[i].a.y, &L[i].b.x, &L[i].b.y);
node.push_back(L[i].a); node.push_back(L[i].b);
}
scanf("%lf%lf", &x, &y);
int ans = 1e9;
REP(i, 0, node.size())
{
int cnt = 1;
_for(j, 1, n)
if(intersect(L[j].a, L[j].b, point(x, y), node[i]))
cnt++;
ans = min(ans, cnt);
}
printf("Number of doors = %d\n", ans);
return 0;
}
周二 4.27(计算几何基础)
poj 1410(判断线段严格相交)
这题看起来很水,但是坑很多
1.输入矩形的坐标要处理一下
2.线段完全在矩形里也算,感觉题目没讲清楚,我以为是边界相交
然后我发现我之前的模板的一个错误
如果不严格相交,不包括交点的话,就是小于0就好
如果严格相交,包括交点,那首先要改成小于等于0
其次共线要特判,因为有共线但是不相交的情况,这种情况下为0
所以就共线的时候特判一下有没有相交,可以用点乘
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const double eps = 1e-8;
struct point
{
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
};
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }
double operator * (point a, point b) { return a.x * b.x + a.y * b.y; }
int dcmp(double t)
{
if(fabs(t) < eps) return 0;
if(t > 0) return 1;
return -1;
}
bool intersect(point a, point b, point c, point d)
{
if(dcmp((a - b) ^ (c - d)) == 0) //共线的情况要特判 坑
{
if((a - c) * (b - c) > 0 && (a - d) * (b - d) > 0) return false; //点乘均为正则共线但是不相交
return true;
}
double t1 = ((a - c) ^ (b - c)) * ((a - d) ^ (b - d));
double t2 = ((c - a) ^ (d - a)) * ((c - b) ^ (d - b));
return dcmp(t1) <= 0 && dcmp(t2) <= 0;
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
double x1, y1, x2, y2;
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
point a(x1, y1), b(x2, y2);
double x3, y3, x4, y4;
scanf("%lf%lf%lf%lf", &x3, &y3, &x4, &y4);
if(x4 < x3) swap(x4, x3); //坑
if(y4 < y3) swap(y4, y3);
bool ok = 0;
if(intersect(a, b, point(x3, y3), point(x4, y3))) ok = 1;
if(intersect(a, b, point(x3, y3), point(x3, y4))) ok = 1;
if(intersect(a, b, point(x3, y4), point(x4, y4))) ok = 1;
if(intersect(a, b, point(x4, y3), point(x4, y4))) ok = 1;
if(x3 <= x1 && x1 <= x4 && y3 <= y1 && y1 <= y4 && x3 <= x2 && x2 <= x4 && y3 <= y2 && y2 <= y4) ok = 1; //线段在矩形里面,巨坑
if(ok) puts("T");
else puts("F");
}
return 0;
}
poj 1696(计算夹角)
这题读完题就想到了凸包,想用凸包的那个算法
后来发现不太不一样,这是个螺旋,不是凸包
然后我突然发现所有点都可以选到,每次就选择最外围的那个,一直往里面绕圈就行
要做到这一点,就选择一个夹角最小的就可以了
注意可能有夹角相同的情况,这个时候选近的,这是一个坑
#include<cstdio>
#include<cmath>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const double eps = 1e-8;
const int N = 60;
struct point
{
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
}p[N];
int vis[N], ans[N], top, n, start, t;
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
double operator * (point a, point b) { return a.x * b.x + a.y * b.y; }
double length(point a)
{
return sqrt(a.x * a.x + a.y * a.y);
}
double angle(point a, point b) //用acos求出夹角
{
return acos((a * b) / (length(a) * length(b)));
}
int dcmp(double x)
{
if(fabs(x) < eps) return 0;
if(x > 0) return 1;
return -1;
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
top = 0;
memset(vis, 0, sizeof(vis));
double mi = 1e308;
scanf("%d", &n);
_for(i, 1, n)
{
scanf("%d%lf%lf", &t, &p[i].x, &p[i].y);
if(mi > p[i].y)
{
mi = p[i].y;
start = i;
}
}
p[0] = point(0, mi); //初始点
ans[0] = 0;
ans[++top] = start;
vis[start] = 1;
while(top < n)
{
mi = 1e308; int x;
_for(i, 1, n)
if(!vis[i])
{
double rad = angle(p[ans[top]] - p[ans[top - 1]], p[i] - p[ans[top]]);
if(mi > rad || dcmp(mi - rad) == 0 && length(p[ans[top]] - x) > length(p[ans[top]] - i)) //注意,计算几何里面共线是一个常见的坑
{
mi = rad;
x = i;
}
}
ans[++top] = x;
vis[x] = 1;
}
printf("%d", top);
_for(i, 1, top) printf(" %d", ans[i]);
puts("");
}
return 0;
}
周三 4.28
最近可能有出去比赛的机会,所以今天在整理模板,我翻了翻以前我每周写的笔记
发现下学期的训练量大了挺多,做的题多了很多,而上学期的训练有些杂乱无章
看来经过了一个学期,我的时间管理技巧好了很多,每天都有一定的训练量
最近就复习以前学的各种算法,先熟悉模板,然后熟悉各种变形
poj 3347(思维)
这道题我是看题解的,主要卡在不知道正方形的左右端点该怎么算
一看题解挺骚的
当前正方形一定是在某个之前的正方形旁边的
而如果考虑放在之前每个正方形旁边,那最终一定是放在最右的那个了
这样就可以算出左右端点了
然后就比较简单了,要覆盖必须边长更大,所以枚举其他更大正方形,更新左右端点的大小
最后如果l < r就可以看见
注意这里可能会有l == r的,而又是浮点数,所以要考虑eps
#include<cstdio>
#include<cmath>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 60;
const double eps = 1e-8;
double l[N], r[N], s[N];
int n;
int dcmp(double x)
{
if(fabs(x) < eps) return 0;
if(x > 0) return 1;
return -1;
}
int main()
{
while(scanf("%d", &n) && n)
{
_for(i, 1, n)
{
scanf("%lf", &s[i]);
double mx = 0;
_for(j, 1, i - 1)
mx = max(mx, r[j] - fabs(s[j] - s[i]) / sqrt(2));
l[i] = mx; r[i] = l[i] + s[i] * sqrt(2);
}
_for(i, 1, n)
{
_for(j, 1, i - 1)
if(s[j] > s[i])
l[i] = max(l[i], r[j]);
_for(j, i + 1, n)
if(s[j] > s[i])
r[i] = min(r[i], l[j]);
if(dcmp(l[i] - r[i]) < 0) printf("%d ", i); //浮点数写比较的时候,尤其是可能相等的时候,就要考虑eps。这里是有可能相等的
}
puts("");
}
return 0;
}
周四 4.29(计算几何基础)
poj 2826(分类讨论 + 精度)
这题我是独立想出来的,各种情况都考虑到了,尤其是想到了被遮挡的情况
但是我因为精度问题过不了,最后看题解,ans改成ans + eps就过了
这篇博客谈计算几何中的浮点误差谈得很好,也提到这道题目https://blog.csdn.net/qq_40791842/article/details/96511961
如下为此博客中内容
现在考虑一种情况,题目要求输出保留两位小数。有个case的正确答案的精确值是0.005,按理应该输出0.01,但你的结果可能是0.005000000001(恭喜),也有可能是0.004999999999(悲剧),如果按照printf(“%.2lf”, a)输出,那你的遭遇将和括号里的字相同。
解决办法是,如果a为正,则输出a+eps, 否则输出a-eps
典型案例: POJ2826
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const double eps = 1e-8;
struct point
{
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
};
int n;
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
point operator * (point a, double k) { return {a.x * k, a.y * k}; }
point operator / (point a, double k) { return {a.x / k, a.y / k}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }
int dcmp(double x)
{
if(fabs(x) < eps) return 0;
if(x > 0) return 1;
return -1;
}
bool intersect(point a, point b, point c, point d)
{
if(dcmp((a - b) ^ (c - d)) == 0) return false; //判断是否相交的时候注意共线的情况
double t1 = ((a - c) ^ (b - c)) * ((a - d) ^ (b - d));
double t2 = ((c - a) ^ (d - a)) * ((c - b) ^ (d - b));
return dcmp(t1) <= 0 && dcmp(t2) <= 0;
}
point make_point(point a, point b, point c, point d)
{
double s1 = (a - c) ^ (d - c);
double s2 = (b - c) ^ (d - c);
return (b * s1 - a * s2) / (s1 - s2);
}
int main()
{
scanf("%d", &n);
_for(i, 1, n)
{
double x1, y1, x2, y2, x3, y3, x4, y4;
scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);
point a(x1, y1), b(x2, y2), c(x3, y3), d(x4, y4);
if(!intersect(a, b, c, d))
{
puts("0.00");
continue;
}
point p = make_point(a, b, c, d);
vector<point> ve;
if(dcmp(y1 - p.y) > 0) ve.push_back(a);
if(dcmp(y2 - p.y) > 0) ve.push_back(b);
if(dcmp(y3 - p.y) > 0) ve.push_back(c);
if(dcmp(y4 - p.y) > 0) ve.push_back(d);
if(ve.size() != 2)
{
puts("0.00");
continue;
}
if(ve[1].y < ve[0].y) swap(ve[1], ve[0]);
if(dcmp((ve[0].x - p.x) * (ve[1].x - p.x)) > 0 && dcmp(ve[1].y - ve[0].y) != 0)
if(dcmp(ve[0].x - p.x) < 0 && ((ve[1] - p) ^ (ve[0] - p)) > 0 && dcmp(ve[1].x - ve[0].x) <= 0 ||
dcmp(ve[0].x - p.x) > 0 && ((ve[1] - p) ^ (ve[0] - p)) < 0 && dcmp(ve[1].x - ve[0].x) >= 0)
{
puts("0.00");
continue;
}
double ans = ((ve[0] - p) ^ (ve[1] - p)) / 2;
printf("%.2f\n", fabs(ans) * (ve[0].y - p.y) / (ve[1].y - p.y) + eps);
}
return 0;
}
周六 5.1(五一集训)
五一每天一场训练赛
每天就是补题和相关知识点
今天发挥不好
带着一股气猛刷题
HDU 6533(预处理 + 快速乘)
这题推公式就好了
注意题目的定义,小心被翻译误导
注意几点
(1)可以预处理子树的个数。我尝试了等比数列求和,但是发现要用逆元,而p不一定为质数,很麻烦。发现可以直接预处理
(2)中间一个地方会爆long long 用快速乘
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll a[N], n, cal[N], p;
int k, m;
ll add(ll a, ll b) { return (a + b) % p; }
ll mul(ll a, ll b)
{
ll res = 0;
for(; b; b >>= 1)
{
if(b & 1) res = add(res, a);
a = a * 2 % p;
}
return res;
}
int main()
{
while(~scanf("%d%d%d%lld", &k, &m, &n, &p))
{
_for(i, 1, k) scanf("%lld", &a[i]);
sort(a + 1, a + k + 1);
cal[0] = 1; ll r = 1;
_for(i, 1, m - 2)
{
r = mul(r, n);
cal[i] = add(cal[i - 1], r);
}
int pos = 1, cnt = 1;
ll ans = 0;
_for(now, 2, m)
{
cnt *= n;
ll sum = 0;
_for(j, pos, pos + cnt - 1) sum = add(sum, a[j]);
pos += cnt;
ans = add(ans, mul(sum, cal[m - now]));
}
printf("%lld\n", ans);
}
return 0;
}
然后猛补莫队
P3901 数列找不同(莫队裸题)
发现之前我已经思考过类似的莫队的问题了,解决问题的关键在于分块
莫队用来处理区间问题,主要是利用之前处理的答案,然后再区间上进行修改左右端点
但是按照左端点排序会很容易超时,直接n方
我们可以把左端点放到块中,用到了分块的思想
就可以优化
一共根号n个块,每次移动最多n,所以复杂度是n根号n的
排序的时候左端点按照块排序,右端点可以奇偶块不同防卡,奇偶优化
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
struct query
{
int l, r, id, bl;
}q[N];
int a[N], cnt[N], ans[N], n, m, sum;
bool cmp(query a, query b)
{
if(a.bl != b.bl) return a.bl < b.bl;
if(a.bl & 1) return a.r < b.r; //奇偶优化 防卡
return a.r > b.r;
}
void add(int x)
{
if(++cnt[a[x]] == 1) sum++; //这里是数不是下标
}
void erase(int x)
{
if(--cnt[a[x]] == 0) sum--; //cnt数组记录每个数出现了多少次
}
int main()
{
scanf("%d%d", &n, &m);
_for(i, 1, n) scanf("%d", &a[i]);
int block_size = sqrt(n);
_for(i, 1, m)
{
int l, r;
scanf("%d%d", &l, &r);
q[i] = {l, r, i, l / block_size};
}
sort(q + 1, q + m + 1, cmp);
int l = 0, r = 0; //l = 0 r = 0 一开始什么都没有,要更新第一个区间
_for(i, 1, m)
{
int ll = q[i].l, rr = q[i].r;
while(l < ll) erase(l++); //erase先删除再移动
while(l > ll) add(--l); //add先移动再加 因为原来已经操作过了
while(r > rr) erase(r--);
while(r < rr) add(++r);
ans[q[i].id] = (sum == (rr - ll + 1)); //这样记录答案不用再排序一次
}
_for(i, 1, m) puts(ans[i] ? "Yes" : "No");
return 0;
}
P1972 [SDOI2009]HH的项链(莫队)
这题是之前训练赛的题
和上一题很像
洛谷卡了,黑暗爆炸没卡
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e6 + 10;
int a[N], ans[N], cnt[N], n, m, sum;
struct query
{
int l, r, id, bl;
}q[N];
bool cmp(query a, query b)
{
if(a.bl != b.bl) return a.bl < b.bl;
if(a.bl & 1) return a.r < b.r;
return a.r > b.r;
}
inline void add(int x)
{
if(++cnt[a[x]] == 1) sum++;
}
inline void del(int x)
{
if(--cnt[a[x]] == 0) sum--;
}
int main()
{
scanf("%d", &n);
_for(i, 1, n) scanf("%d", &a[i]);
int block = sqrt(n);
scanf("%d", &m);
_for(i, 1, m)
{
int l, r;
scanf("%d%d", &l, &r);
q[i] = {l, r, i, l / block};
}
sort(q + 1, q + m + 1, cmp);
int l = 0, r = 0;
_for(i, 1, m)
{
int ll = q[i].l, rr = q[i].r;
while(l < ll) del(l++);
while(l > ll) add(--l);
while(r < rr) add(++r);
while(r > rr) del(r--);
ans[q[i].id] = sum;
}
_for(i, 1, m) printf("%d\n", ans[i]);
return 0;
}
CF86D Powerful array(莫队拓展)
就处理贡献的时候手算一下就好
要开long long WA了一发
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int cnt[N], n, m;
ll a[N], ans[N], sum;
struct query
{
int l, r, id, bl;
}q[N];
bool cmp(query a, query b)
{
if(a.bl != b.bl) return a.bl < b.bl;
if(a.bl & 1) return a.r < b.r;
return a.r > b.r;
}
void add(int x)
{
sum += a[x] * (2 * cnt[a[x]] + 1);
cnt[a[x]]++;
}
void del(int x)
{
cnt[a[x]]--;
sum -= a[x] * (2 * cnt[a[x]] + 1);
}
int main()
{
scanf("%d%d", &n, &m);
_for(i, 1, n) scanf("%lld", &a[i]);
int block = sqrt(n);
_for(i, 1, m)
{
int l, r;
scanf("%d%d", &l, &r);
q[i] = {l, r, i, l / block};
}
sort(q + 1, q + m + 1, cmp); //记得排序预处理
int l = 0, r = 0;
_for(i, 1, m)
{
int ll = q[i].l, rr = q[i].r;
while(l < ll) del(l++);
while(l > ll) add(--l);
while(r < rr) add(++r);
while(r > rr) del(r--);
ans[q[i].id] = sum;
}
_for(i, 1, m) printf("%lld\n", ans[i]);
return 0;
}
HDU 6534(莫队 + 树状数组 + 离散化)
离散化的时候要保留一下相差为k的信息
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
const int N = 6e4 + 10;
int a[N], ans[N], n, m, sum, k;
int L[N], R[N], f[N];
vector<int> ve;
struct query
{
int l, r, id, bl;
}q[N];
int lowbit(int x) { return x & (-x); }
void modify(int x, int p)
{
for(; x < N; x += lowbit(x))
f[x] += p;
}
int Sum(int x)
{
int res = 0;
for(; x; x -= lowbit(x))
res += f[x];
return res;
}
bool cmp(query a, query b)
{
if(a.bl != b.bl) return a.bl < b.bl;
if(a.bl & 1) return a.r < b.r;
return a.r > b.r;
}
int ask(int l, int r) { return Sum(r) - Sum(l - 1); }
void add(int x)
{
sum += ask(L[x], R[x] - 1);
modify(a[x], 1);
}
void del(int x)
{
modify(a[x], -1);
sum -= ask(L[x], R[x] - 1);
}
void lsh()
{
ve.push_back(-1e9); //让最小下标为1 最小值打进去
sort(ve.begin(), ve.end());
ve.erase(unique(ve.begin(), ve.end()), ve.end()); //注意写法
_for(i, 1, n)
{
L[i] = lower_bound(ve.begin(), ve.end(), a[i] - k) - ve.begin();
R[i] = upper_bound(ve.begin(), ve.end(), a[i] + k) - ve.begin(); //r右端 + 1
}
_for(i, 1, n) a[i] = lower_bound(ve.begin(), ve.end(), a[i]) - ve.begin();
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
_for(i, 1, n)
{
scanf("%d", &a[i]);
ve.push_back(a[i]);
}
int block = sqrt(n);
_for(i, 1, m)
{
int l, r;
scanf("%d%d", &l, &r);
q[i] = {l, r, i, l / block};
}
sort(q + 1, q + m + 1, cmp);
lsh();
int l = 1, r = 0; //l = 1好一些 l=0会多删一个0
_for(i, 1, m)
{
int ll = q[i].l, rr = q[i].r;
while(l < ll) del(l++);
while(l > ll) add(--l);
while(r < rr) add(++r);
while(r > rr) del(r--);
ans[q[i].id] = sum;
}
_for(i, 1, m) printf("%d\n", ans[i]);
return 0;
}
五一七天就这么搞
训练赛 + 疯狂补题
poj 1039(计算几何基础 + 细节)
这道题卡了好久
有了之前的经验,很快想到枚举端点的直线
但是实现的时候很多细节没考虑到
对于当前的直线
就一段一段判断,看有没有相交,再第一个交点那里停下来,判断是否能更新答案
这里又写坑
(1)x坐标可能为负数,所以ans初始化要为最小值
(2)判相交的时候是不严格相交。这就导致了可能从端点处穿出去而判断错误
这个时候为了避免这种情况
换一种方法,用叉积判断上下两个线段是否在当前直线的同侧。
这种方法就可以避免这种情况
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<vector>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 30;
const double eps = 1e-8;
struct point
{
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
}p[N][2];
int n;
point operator - (point a, point b) { return point(a.x - b.x, a.y - b.y); }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }
point operator * (point a, double k) { return point(a.x * k, a.y * k); }
point operator / (point a, double k) { return point(a.x / k, a.y / k); }
int dcmp(double x)
{
if(fabs(x) < eps) return 0;
if(x > 0) return 1;
return -1;
}
bool is_intersect(point a, point b, point c, point d)
{
return dcmp(((a - c) ^ (b - c)) * ((a - d) ^ (b - d))) < 0;
}
point make_point(point a, point b, point c, point d)
{
double s1 = (a - c) ^ (d - c);
double s2 = (b - c) ^ (d - c);
return (b * s1 - a * s2) / (s1 - s2);
}
int main()
{
while(~scanf("%d", &n) && n)
{
_for(i, 1, n)
{
scanf("%lf%lf", &p[i][1].x, &p[i][1].y);
p[i][0] = point(p[i][1].x, p[i][1].y - 1);
}
double ans = -1e300; //负数坑
bool ok = 0;
_for(i, 1, n)
_for(g, 0, 1)
_for(j, i + 1, n)
_for(r, 0, 1)
{
if(g == r) continue;
point a = p[i][g], b = p[j][r];
int pos = 1;
for(; pos < n; pos++) //这里的条件注意。前后两个端点都要
{
if(((a - b) ^ (p[pos + 1][1] - a)) * ((a - b) ^ (p[pos + 1][0] - a)) > eps) break;
if(((a - b) ^ (p[pos][1] - a)) * ((a - b) ^ (p[pos][0] - a)) > eps) break;
}
if(pos == n)
{
ok = 1;
break;
}
if(pos >= j)
{
point t;
if(is_intersect(a, b, p[pos][1], p[pos + 1][1]))
t = make_point(a, b, p[pos][1], p[pos + 1][1]);
else t = make_point(a, b, p[pos][0], p[pos + 1][0]);
ans = max(ans, t.x);
}
}
if(ok) puts("Through all the pipe.");
else printf("%.2f\n", ans);
}
return 0;
}
周日 5.2(五一集训)
今天依然训练赛
发挥不好,做得磕磕绊绊的
疯狂补题
A Math Problem(坑)
这是一道签到题,但是我WA了好多次
16的16次方是爆long long的也爆unsigned long long的
然后我用了double 然而我忘记了double的有效位数是有限的,所以当值很大的时候double的值误差挺大的
所以依然wa
最后我是特判了一下,大于15的15次方就输出15
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef unsigned long long ull;
ull a[20];
int main()
{
_for(i, 1, 16)
{
a[i] = 1;
_for(j, 1, i) a[i] *= i;
}
ull n;
while(~scanf("%llu", &n))
{
if(n >= a[15]) printf("15\n");
else
{
_for(i, 1, 15)
if(n < a[i + 1])
{
printf("%d\n", i);
break;
}
}
}
return 0;
}
Covering(递推 + 矩阵快速幂)
这道题做了好久
静下心来推公式,化简,最后得出一个递推公式,然后矩阵快速幂就好了
注意构造的矩阵有负数,所以结果有负数,最后输出的时候要特判一下ans < 0就加上p
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int mod = 1000000007;
struct node
{
ll s[5][5];
node() { memset(s, 0, sizeof(s)); }
};
node operator * (node a, node b)
{
node c;
_for(i, 1, 4)
_for(j, 1, 4)
_for(r, 1, 4)
c.s[i][j] = (c.s[i][j] + (a.s[i][r] * b.s[r][j]) % mod) % mod;
return c;
}
node binpow(node a, ll b)
{
node res;
_for(i, 1, 4) res.s[i][i] = 1;
for(; b; b >>= 1)
{
if(b & 1) res = res * a;
a = a * a;
}
return res;
}
int main()
{
ll n;
while(~scanf("%lld", &n))
{
node k;
k.s[1][4] = -1;
k.s[2][1] = 1; k.s[2][4] = 1;
k.s[3][2] = 1, k.s[3][4] = 5;
k.s[4][3] = 1; k.s[4][4] = 1;
ll ans = 0, h[5];
h[0] = 1, h[1] = 1, h[2] = 5, h[3] = 11;
if(n <= 3)
{
printf("%lld\n", h[n]);
continue;
}
k = binpow(k, n - 3);
_for(r, 1, 4) ans = (ans + h[r - 1] * k.s[r][4] % mod) % mod;
if(ans < 0) ans += mod;
printf("%lld\n", ans);
}
return 0;
}
Duizi and Shunzi(思维)
这是一道思维题
但是我考试时就是没有想出来
我第一感觉是尽量多的对子,先取完,然后最后剩下的数看有没有顺子
但是这样过不了最后一个样例,因为有时候选择了一个对子会少掉两个顺子
我不知道该怎么处理
正解的思路是尽可能取对子,这点我想对了,但是顺子的处理方式我没想到
从小到大遍历一遍,都取对子,然后如果当前数的前两个都有1而且当前这个数有的话,就取顺子
这样就把之前多的利用起来了,也避免了最后一个样例的情况
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e6 + 10;
int cnt[N], n;
int main()
{
while(~scanf("%d", &n))
{
memset(cnt, 0, sizeof(cnt));
_for(i, 1, n)
{
int x; scanf("%d", &x);
cnt[x]++;
}
int ans = 0;
_for(i, 1, n)
{
if(i >= 3 && cnt[i - 2] && cnt[i - 1] && cnt[i])
{
ans++;
cnt[i - 2]--;
cnt[i - 1]--;
cnt[i]--;
}
ans += cnt[i] / 2;
cnt[i] %= 2;
}
printf("%d\n", ans);
}
return 0;
}
Query on A Tree(离线 + 字典树合并)
这题看到可以用可持久化Trie做,之后补一下相关知识点,比如主席树
这题首先是离线处理,题目中关于询问的,离线处理是一个重要思想
离线了之后意味着可以dfs一遍,回溯的时候统计答案,这样保证了子树
然后对每一个节点的值建立字典树,之后字典树合并
注意一开始每个点的字典树的根节点就是标号
合并的时候比较骚,要用递归的思想来合并
都有这个节点就继续往下,其中一个没有就返回
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
int t[N * 35][2], val[N], ans[N], n, q, cnt;
vector<pair<int, int> > p[N];
vector<int> g[N];
void add(int root, int x)
{
int p = root;
for(int i = 31; i >= 0; i--)
{
int now = (x >> i) & 1; //取出x的第i位 从第0位到第31位。注意开始是第0位不是第1位
if(!t[p][now]) t[p][now] = ++cnt;
p = t[p][now];
}
}
int query(int root, int x)
{
int p = root, res = 0;
for(int i = 31; i >= 0; i--)
{
int now = (x >> i) & 1;
if(t[p][now ^ 1])
{
res |= 1 << i; //注意不要写错 1 << i就是2的i次方,就是第i位
p = t[p][now ^ 1];
}
else p = t[p][now];
}
return res;
}
int Union(int u, int v) //这个合并要递归
{
if(u == 0) return v;
if(v == 0) return u;
t[u][0] = Union(t[u][0], t[v][0]);
t[u][1] = Union(t[u][1], t[v][1]);
return u;
}
void dfs(int u)
{
add(u, val[u]);
for(auto v: g[u])
{
dfs(v);
Union(u, v);
}
for(auto x: p[u]) ans[x.first] = query(u, x.second);
}
int main()
{
while(~scanf("%d%d", &n, &q))
{
cnt = n; //关键,每个点的初始节点都已经设置好了
_for(i, 1, n)
{
scanf("%d", &val[i]);
g[i].clear();
p[i].clear();
}
memset(t, 0, sizeof(t));
_for(i, 1, n - 1)
{
int x; scanf("%d", &x);
g[x].push_back(i + 1);
}
_for(i, 1, q)
{
int u, x;
scanf("%d%d", &u, &x);
p[u].push_back(make_pair(i, x));
}
dfs(1);
_for(i, 1, q) printf("%d\n", ans[i]);
}
return 0;
}
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
class B //含有纯虚函数的类是抽象类,没有对象。只能用指针
{
public:
virtual void print() = 0; //纯虚函数
};
class B1: public B
{
public:
void print() { puts("B1"); }
};
void out2(B *b)
{
b->print();
}
int main()
{
B1 b;
out2(&b);
B *p; //第二种方式
p = &b;
p->print();
return 0;
}