3.3

存一下作业的题解

SWERC-2019 - J – Counting Trees
满足中序遍历必须选择最小的点作为根
若是点权值都不等则选择唯一
当存在多个最小值时,可以任意组合这些最小值点
也就是满足贡献卡特兰数

然后我们就找不被更小值隔开的的数的长度 累乘进答案就行
可以拿个单调栈来求
具体看代码实现

include

include

include

include

include

include

define LL long long

define REP(i, x, y) for(LL i = (x);i <= (y);i++)

using namespace std;
LL RD(){
LL out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const LL maxn = 1000010, M = 1000000007;
LL num, MAX;
LL a[maxn], inv[maxn], C[maxn];
LL S[maxn], top;
LL cnt[maxn];
LL pre[maxn];
void init(){
num = RD();
REP(i, 1, num)a[i] = RD(), MAX = max(MAX, a[i]);
inv[1] = 1;
REP(i, 2, num + 1)inv[i] = (M - M / i) * inv[M % i] % M;//递推逆元
C[0] = C[1] = 1;
REP(i, 2, num){//递推卡特兰数
C[i] = C[i - 1] * inv[i] % M;
C[i] = C[i] * (2 * i - 1) % M;
C[i] = C[i] * (2 * i) % M;
C[i] = C[i] * inv[i + 1] % M;
}
a[0] = -1;
S[++top] = 0;//保证一个最小左端点
REP(i, 1, num){
while(a[S[top]] > a[i])top--;
pre[i] = S[top];
S[++top] = i;
}
}
void work(){
LL ans = 1;
REP(i, 1, num){
if(a[pre[i]] < a[i]){
ans = ans * C[cnt[a[i]]] % M;
cnt[a[i]] = 1;
}
else cnt[a[i]]++;
}
REP(i, 0, MAX)ans = ans * C[cnt[i]] % M;
cout<<ans<<endl;
}
int main(){
init();
work();
return 0;
}

POJ1177 IOI 1998, Picture

扫描线求周长。
我是横竖都扫了一遍, 也是可以通过维护扫描线截得线段数来只扫一次的

include

include

include

include

include

include

define LL long long

define REP(i, x, y) for(LL i = (x);i <= (y);i++)

using namespace std;
LL RD(){
LL out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const LL maxn = 100010;
LL num, X[maxn << 1];
LL tot;//去重后的横坐标数
struct ScanLine{
LL h, l, r, mark;
bool operator < (const ScanLine &x) const {
return h < x.h;
}
}line[maxn << 1];

define lid (id << 1)

define rid (id << 1) | 1

struct Seg_Tree{
LL l, r, tag, len;
}tree[maxn << 4];
void pushup(LL id){
LL l = tree[id].l, r = tree[id].r;
if(tree[id].tag)
tree[id].len = X[r + 1] - X[l];
else
tree[id].len = tree[lid].len + tree[rid].len;
}
void build(LL id, LL l, LL r){
tree[id].l = l, tree[id].r = r;
if(l == r){
tree[id].tag = tree[id].len = 0;
return ;
}
LL mid = (l + r) >> 1;
build(lid, l, mid), build(rid, mid + 1, r);
pushup(id);
}
void update(LL id, LL L, LL R, LL val){
LL l = tree[id].l, r = tree[id].r;
if(X[l] >= R || X[r + 1] <= L)return ;
if(X[l] >= L && X[r + 1] <= R){
tree[id].tag += val;
pushup(id);
return ;
}
update(lid, L, R, val);
update(rid, L, R, val);
pushup(id);
}
int data[maxn << 2], cnt;
void read(){
num = RD();
REP(i, 1, num * 4)data[++cnt] = RD();
}
//LL abs(LL x){}
void init1(){
//num = RD();
int j = 0;
REP(i, 1, num){
LL x1 = data[++j], y1 = data[++j], x2 = data[++j], y2 = data[++j];
X[i * 2 - 1] = x1, X[i << 1] = x2;
line[i * 2 - 1] = (ScanLine){y1, x1, x2, 1};
line[i << 1] = (ScanLine){y2, x1, x2, -1};
}
num = num << 1;
sort(X + 1, X + 1 + num);
sort(line + 1, line + 1 + num);
tot = unique(X + 1, X + 1 + num) - X - 1;
build(1, 1, tot - 1);
}
void init2(){
//num = RD();
int j = 0;
REP(i, 1, num){
LL y1 = data[++j], x1 = data[++j], y2 = data[++j], x2 = data[++j];
X[i * 2 - 1] = x1, X[i << 1] = x2;
line[i * 2 - 1] = (ScanLine){y1, x1, x2, 1};
line[i << 1] = (ScanLine){y2, x1, x2, -1};
}
num = num << 1;
sort(X + 1, X + 1 + num);
sort(line + 1, line + 1 + num);
tot = unique(X + 1, X + 1 + num) - X - 1;
build(1, 1, tot - 1);
}
LL ans;
void work(){
LL temp = 0;
REP(i, 1, num){
update(1, line[i].l, line[i].r, line[i].mark);
LL cao = temp - tree[1].len;
if(cao < 0)cao = -cao;
ans += cao;
temp = tree[1].len;
}
//
}
int main(){
read();
init1();
work();
init2();
work();
cout<<ans<<endl;
return 0;
}

Atlantis, HDU1542
扫描线求面积
注意浮点数和多组初始化即可

include

include

include

include

include

include

define LL long long

define REP(i, x, y) for(LL i = (x);i <= (y);i++)

using namespace std;
LL RD(){
LL out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const LL maxn = 100010;
LL num, cnt;
double X[maxn << 1];
LL tot;//去重后的横坐标数
struct ScanLine{
double h, l, r, mark;
bool operator < (const ScanLine &x) const {
return h < x.h;
}
}line[maxn << 1];

define lid (id << 1)

define rid (id << 1) | 1

struct Seg_Tree{
int l, r;
double tag, len;
}tree[maxn << 4];
void pushup(LL id){
LL l = tree[id].l, r = tree[id].r;
if(tree[id].tag)
tree[id].len = X[r + 1] - X[l];
else
tree[id].len = tree[lid].len + tree[rid].len;
}
void build(LL id, LL l, LL r){
tree[id].l = l, tree[id].r = r;
tree[id].len = tree[id].tag = 0;
if(l == r){
tree[id].tag = tree[id].len = 0;
return ;
}
LL mid = (l + r) >> 1;
build(lid, l, mid), build(rid, mid + 1, r);
pushup(id);
}
void update(LL id, double L, double R, LL val){
LL l = tree[id].l, r = tree[id].r;
if(X[l] >= R || X[r + 1] <= L)return ;
if(X[l] >= L && X[r + 1] <= R){
tree[id].tag += val;
pushup(id);
return ;
}
update(lid, L, R, val);
update(rid, L, R, val);
pushup(id);
}
void init(){
REP(i, 1, num){
//LL x1 = RD(), y1 = RD(), x2 = RD(), y2 = RD();
double x1, x2, y1, y2;
cin>>x1>>y1>>x2>>y2;
X[i * 2 - 1] = x1, X[i << 1] = x2;
line[i * 2 - 1] = (ScanLine){y1, x1, x2, 1};
line[i << 1] = (ScanLine){y2, x1, x2, -1};
}
num = num << 1;
sort(X + 1, X + 1 + num);
sort(line + 1, line + 1 + num);
tot = unique(X + 1, X + 1 + num) - X - 1;
build(1, 1, tot - 1);
}
void work(){
double ans = 0;
REP(i, 1, num - 1){
update(1, line[i].l, line[i].r, line[i].mark);
ans += tree[1].len * (line[i + 1].h - line[i].h);
}
printf("Test case #%d\n", cnt);
printf("Total explored area: %.2f\n", ans);
puts("");
//cout<<ans<<endl;
}
int main(){
while(cin>>num){
cnt++;
if(!num)return 0;
init();
work();
}
return 0;
}

The Skyline Problem, UVa105
我得想法是,从左到右扫描线,因为下断点唯一,所以长度唯一决定扫描线形状
于是遇见与上一条扫描线长度不同就输出

include

include

include

include

include

include

define LL long long

define REP(i, x, y) for(LL i = (x);i <= (y);i++)

using namespace std;
LL RD(){
LL out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const LL maxn = 100010;
LL num, X[maxn << 1];
LL tot;//去重后的横坐标数
struct ScanLine{
LL h, l, r, mark;
bool operator < (const ScanLine &x) const {
return h < x.h;
}
}line[maxn << 1];

define lid (id << 1)

define rid (id << 1) | 1

struct Seg_Tree{
LL l, r, tag, len;
}tree[maxn << 4];
void pushup(LL id){
LL l = tree[id].l, r = tree[id].r;
if(tree[id].tag)
tree[id].len = X[r + 1] - X[l];
else
tree[id].len = tree[lid].len + tree[rid].len;
}
void build(LL id, LL l, LL r){
tree[id].tag = tree[id].len = 0;
tree[id].l = l, tree[id].r = r;
if(l == r){
tree[id].tag = tree[id].len = 0;
return ;
}
LL mid = (l + r) >> 1;
build(lid, l, mid), build(rid, mid + 1, r);
pushup(id);
}
void update(LL id, LL L, LL R, LL val){
LL l = tree[id].l, r = tree[id].r;
if(X[l] >= R || X[r + 1] <= L)return ;
if(X[l] >= L && X[r + 1] <= R){
tree[id].tag += val;
pushup(id);
return ;
}
update(lid, L, R, val);
update(rid, L, R, val);
pushup(id);
}
void init(){
LL x1, y1, x2, y2;
while(scanf("%lld", &y1) != EOF){
num++;
int i = num;
x1 = 0;
cin>>x2>>y2;
X[i * 2 - 1] = x1, X[i << 1] = x2;
line[i * 2 - 1] = (ScanLine){y1, x1, x2, 1};
line[i << 1] = (ScanLine){y2, x1, x2, -1};
}
/REP(i, 1, num){
LL y1 = RD(), x1 = 0, x2 = RD(), y2 = RD();
X[i * 2 - 1] = x1, X[i << 1] = x2;
line[i * 2 - 1] = (ScanLine){y1, x1, x2, 1};
line[i << 1] = (ScanLine){y2, x1, x2, -1};
}
/
num = num << 1;
sort(X + 1, X + 1 + num);
sort(line + 1, line + 1 + num);
tot = unique(X + 1, X + 1 + num) - X - 1;
build(1, 1, tot - 1);

}

void work(){
LL pre = -1;
REP(i, 1, num){
update(1, line[i].l, line[i].r, line[i].mark);
if(pre == -1 || tree[1].len != pre){
cout<<line[i].h<<" "<<tree[1].len<<" ";
pre = tree[1].len;
}
}
puts("");
}
int main(){
init();
work();
return 0;
}

The Sky is the Limit, UVa1077
本来想着怎么拿扫描线写 斜着不咋会了
一看数据 n才100
暴力模拟
先求出所有关键点,即原本的三个点和线段交点
然后枚举这些关键点的横坐标,纵坐标最高的就是最终选择的点(最高点)
把点连起来就行
最后处理下地平线不计入答案

Most Distant Point from the Sea, Tokyo2007, UVa1396
二分加半平面交
二分出一个值,把所有的边沿着法向量向内移动mid,用新的线段构造半平面交
若存在半平面交则合法
复杂度O(n(logn)^2)

Triathlon,NEERC 2000, POJ1755
选手i要赢得选手j,需要满足式子:
x/xi + y/yi + (1 - x - y)/zi < x / xj + y/yj + (1 - x - y)/zj
是一个关于xy的不等式
于是对于选手i,枚举选手j构造不等式做半平面交
若半平面交非空则可以获胜
总值设为1的话, eps很难定
所以总值设个1e6什么的就好
复杂度O(n^2logn)

丛林警戒队(Jungle Outpost, HDU3761)
想法比较暴力:
首先消灭的点必须连续,消灭一个点相当于消除了一个角,往一个方向推进肯定比往两个方向数量更优
于是二分答案,对于每个mid,枚举起点消灭连续的点,消灭n次构造半平面交
交不为空集则打不掉
复杂度O(n(logn)^2)

posted @ 2021-03-15 20:06  Tony_Double_Sky  阅读(59)  评论(0编辑  收藏  举报