【做题】neerc2017的A、C、I、L
A - Archery Tournament
一开始往化简公式的方向去想,结果没什么用。
考虑与一条垂线相交的圆的个数。不难YY,当圆的个数最多时,大概就是这个样子的:
我们稍微推一下式子,然后就能发现其个数不超过\(O(\log C)\),其中\(C\)为坐标范围(即1e9)。
接下来的事情就简单了,我们把数据离散化后就能用线段树查询过某一条垂线的所有圆了,再暴力判断是哪一个就可以了。
时间复杂度\(O(n \log ^2 n)\)。
#include <bits/stdc++.h>
#define sqr(x) (1ll * (x) * (x))
using namespace std;
const int N = 400010;
struct circle {
int x,y;
bool inside(int a,int b) {
return sqr(x - a) + sqr(b - y) < sqr(y);
}
} cir[N];
int n;
struct data {
int tp,x,y;
} dat[N];
vector<int> tmp,ins;
set<int> q[N << 2];
void modify(int l,int r,int v,int sgn,int x=1,int lp=1,int rp = n) {
if (l > rp || r < lp) return;
if (lp >= l && rp <= r) {
if (sgn) q[x].insert(v);
else q[x].erase(v);
return;
}
int mid = (lp + rp) >> 1;
modify(l,r,v,sgn,x<<1,lp,mid);
modify(l,r,v,sgn,x<<1|1,mid+1,rp);
}
void add(int id,int sgn) {
int l = lower_bound(tmp.begin(),tmp.end(),dat[id].x - dat[id].y) - tmp.begin() + 1;
int r = upper_bound(tmp.begin(),tmp.end(),dat[id].x + dat[id].y) - tmp.begin();
modify(l,r,id,sgn);
}
void query(int p,int x=1,int lp=1,int rp=n) {
if (lp > p || rp < p) return;
for (set<int>::iterator t = q[x].begin() ; t != q[x].end() ; ++ t)
ins.push_back(*t);
if (lp == rp) return;
int mid = (lp + rp) >> 1;
if (p <= mid) query(p,x<<1,lp,mid);
else query(p,x<<1|1,mid+1,rp);
}
int doit(int a,int b) {
ins.clear();
int p = lower_bound(tmp.begin(),tmp.end(),a) - tmp.begin() + 1;
query(p);
for (int i = 0 ; i < (int)ins.size() ; ++ i) {
if (cir[ins[i]].inside(a,b)) {
add(ins[i],0);
return ins[i];
}
}
return -1;
}
int main() {
scanf("%d",&n);
for (int i = 1 ; i <= n ; ++ i) {
scanf("%d%d%d",&dat[i].tp,&dat[i].x,&dat[i].y);
if (dat[i].tp == 2)
tmp.push_back(dat[i].x);
else cir[i].x = dat[i].x, cir[i].y = dat[i].y;
}
sort(tmp.begin(),tmp.end());
tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
for (int i = 1 ; i <= n ; ++ i) {
if (dat[i].tp == 1)
add(i,1);
else printf("%d\n",doit(dat[i].x,dat[i].y));
}
return 0;
}
C - Connections
类比targan,我们考虑构造一棵dfs树。那么,单靠dfs树上的这些边,我们就能保证从根结点能到达任意一个结点。然后就只用让任意一个结点都能到达根结点就好了。于是我们在反图上以同样的点为根结点建dfs树(反图仍然强连通),把这两棵树并在一起,边数最多为\(2n-2\),能满足本题要求。
时间复杂度\(O(n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n,m,a[N],b[N],ned[N],vis[N];
struct edge {
int la,b,id;
};
struct tree {
edge con[N << 1];
int tot,fir[N];
void add(int from,int to,int num) {
con[++tot] = (edge) {fir[from],to,num};
fir[from] = tot;
}
void init() {
tot = 0;
for (int i = 1 ; i <= n ; ++ i)
fir[i] = 0;
}
void dfs(int pos) {
vis[pos] = 1;
for (int i = fir[pos] ; i ; i = con[i].la) {
if (vis[con[i].b]) continue;
ned[con[i].id] = 1;
dfs(con[i].b);
}
}
} g,ig;
int main() {
int T;
scanf("%d",&T);
while (T --) {
scanf("%d%d",&n,&m);
g.init();
ig.init();
for (int i = 1 ; i <= m ; ++ i)
scanf("%d%d",&a[i],&b[i]), g.add(a[i],b[i],i), ig.add(b[i],a[i],i), ned[i] = 0;
for (int i = 1 ; i <= n ; ++ i)
vis[i] = 0;
g.dfs(1);
for (int i = 1 ; i <= n ; ++ i)
vis[i] = 0;
ig.dfs(1);
for (int i = 1, j = 1 ; i <= m && j <= m - 2 * n ; ++ i)
if (!ned[i]) printf("%d %d\n",a[i],b[i]), ++ j;
}
return 0;
}
I - Interactive Sort
首先一个重要的性质:数据随机。
考虑通过偶数比较来把奇数排序。假如我们把任意一个偶数与所有奇数比较,那么我们就能够把奇数分成两组,并且得出这个偶数是什么。把一些数随机地分为两组,这与快速排序十分相似。因此,我们希望能避免把每个偶数都与所有奇数比较。
假设我们现在已经把奇数分为\(k\)组,那么,对于我们新取出的一个偶数\(x\),其中至少有\(k-1\)组满足其中所有元素都大于(或小于)\(x\)。然而,具体地确定被\(x\)划分的是哪一组是很困难的,因为那一组有的元素大于\(x\),有的小于。那我们不妨就只确定到两组,这个可以用二分实现。然后我们就把两组划分为三组。
这个算法本质就是快速排序,只不过多了二分查找。
然后粗糙计算一下询问次数的常数。把两组划分为三组相较于把一组划分为两组大概是2倍常数,二分查找估计为常数加1。快排期望复杂度的常数是2,故这个算法的常数可以认为是5。对于\(n=5000\),常数为5的\(O(n \log n)\)次询问,不会超出本题的限制。
时间复杂度上,暴力维护分组是\(O(n^2)\)的,可以用数据结果做到\(O(n \log^2 n)\)。
#include <bits/stdc++.h>
#define gc() getchar()
using namespace std;
const int N = 10010;
struct data {
int l,r;
} dat[N];
int n,cnt,a[N],tmp[N],tcnt,ans[N];
int ask(int x,int y) {
char tmp;
printf("? %d %d\n",x,y);
fflush(stdout);
for (tmp = gc() ; tmp != '<' && tmp != '>' ; tmp = gc());
return tmp == '<';
}
int main() {
int l,r,mid,p,num;
scanf("%d",&n);
for (int i = 1 ; i <= (n+1)/2 ; ++ i) a[i] = i;
dat[1] = (data) {1,(n+1)/2};
cnt = 1;
for (int i = 1 ; i <= n/2 ; ++ i) {
l = 1, r = cnt;
while (l + 1 < r) {
mid = (l + r) >> 1;
if (ask(i,a[dat[mid].l])) r = mid;
else l = mid;
}
p = dat[l].l;
num = tcnt = 0;
for (int j = dat[l].l ; j <= dat[l].r ; ++ j)
if (ask(i,a[j])) tmp[++tcnt] = a[j];
else a[p++] = a[j], num ++;
for (int j = 1 ; j <= tcnt ; ++ j)
a[p+j-1] = tmp[j];
if (num != 0 && num != dat[l].r - dat[l].l + 1) {
ans[i] = (num + dat[l].l-1) << 1;
++ cnt;
for (int j = cnt ; j >= l + 2 ; -- j)
dat[j] = dat[j-1];
dat[l+1] = (data) {dat[l].l + num,dat[l].r};
dat[l] = (data) {dat[l].l,dat[l].l + num - 1};
continue;
}
if (l == r) {
ans[i] = n;
continue;
}
p = dat[r].l;
num = tcnt = 0;
for (int j = dat[r].l ; j <= dat[r].r ; ++ j)
if (ask(i,a[j])) tmp[++tcnt] = a[j];
else a[p++] = a[j], num ++;
for (int j = 1 ; j <= tcnt ; ++ j)
a[p+j-1] = tmp[j];
ans[i] = (num + dat[r].l - 1) << 1;
if (num == 0 || num == dat[r].r - dat[r].l + 1) continue;
++ cnt;
for (int j = cnt ; j >= r + 2 ; -- j)
dat[j] = dat[j-1];
dat[r+1] = (data) {dat[r].l + num,dat[r].r};
dat[r] = (data) {dat[r].l,dat[r].l + num - 1};
}
printf("!");
for (int i = 1 ; i <= n/2 ; ++ i)
printf(" %d",ans[i]);
for (int i = 1 ; i <= (n+1)/2 ; ++ i)
ans[a[i]] = (i<<1) - 1;
for (int i = 1 ; i <= (n+1)/2 ; ++ i)
printf(" %d",ans[i]);
puts("");
return 0;
}
L - Laminar Family
这题有多个做法,这里仅涉及我本人的做法。
我们考虑按长度从大到小枚举路径,那么当我们枚举到路径\(k\)时,我们就不需要考虑\(k\)包含其他路径的情况,因为能被\(k\)包含的路径还没加进来。那么,\(k\)这一条路径要么已经完全被其他另一条路径覆盖了,要么完全没被覆盖。于是,我们枚举路径时对这条路径上的所有结点都染色(不同路径的颜色不同),那么答案是Yes就等价于,对于所有路径,它上面的结点在染上它的颜色之前的颜色都是相同的(或都没有颜色)。这个可以用树链剖分+线段树实现。
时间复杂度\(O(n \log ^2 n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
struct edge {
int la,b;
} con[N << 1];
int tot,fir[N];
void add(int from,int to) {
con[++tot] = (edge) {fir[from],to};
fir[from] = tot;
}
int fa[N],sz[N],hson[N],top[N],dfn[N],dep[N],cnt,n,m,ans;
void dfs_init(int pos) {
dep[pos] = dep[fa[pos]] + 1;
sz[pos] = 1;
for (int i = fir[pos] ; i ; i = con[i].la) {
if (con[i].b == fa[pos]) continue;
fa[con[i].b] = pos;
dfs_init(con[i].b);
sz[pos] += sz[con[i].b];
if (sz[con[i].b] > sz[hson[pos]])
hson[pos] = con[i].b;
}
}
void dfs_build(int pos,int tp) {
top[pos] = tp;
dfn[pos] = ++cnt;
if (hson[pos]) dfs_build(hson[pos],tp);
for (int i = fir[pos] ; i ; i = con[i].la) {
if (con[i].b == fa[pos] || con[i].b == hson[pos])
continue;
dfs_build(con[i].b,con[i].b);
}
}
int lca(int x,int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x,y);
return x;
}
struct node {
int tag,val;
} t[N << 2];
inline void puttag(int x,int v) {
t[x].tag = v;
t[x].val = v;
}
void push_down(int x) {
if (!t[x].tag) return;
puttag(x<<1,t[x].tag);
puttag(x<<1|1,t[x].tag);
t[x].tag = 0;
}
inline int merge(int x,int y) {
if (x == -2) return y;
if (y == -2) return x;
if (x == -1 || y == -1) return -1;
if (x != y) return -1;
return x;
}
void push_up(int x) {
if (t[x].tag) push_down(x);
t[x].val = merge(t[x<<1].val,t[x<<1|1].val);
}
void modify(int l,int r,int v,int x=1,int lp=1,int rp=n) {
if (l > rp || r < lp) return;
if (lp >= l && rp <= r)
return (void)(puttag(x,v));
int mid = (lp + rp) >> 1;
push_down(x);
modify(l,r,v,x<<1,lp,mid);
modify(l,r,v,x<<1|1,mid+1,rp);
push_up(x);
}
int query(int l,int r,int x=1,int lp=1,int rp=n) {
if (l > rp || r < lp) return -2;
if (lp >= l && rp <= r)
return t[x].val;
int mid = (lp + rp) >> 1;
push_down(x);
return merge(query(l,r,x<<1,lp,mid),query(l,r,x<<1|1,mid+1,rp));
}
bool doit(int x,int y,int id) {
int res = 1, tmp = -2;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x,y);
tmp = merge(tmp,query(dfn[top[x]],dfn[x]));
if (tmp == -1) res = 0;
modify(dfn[top[x]],dfn[x],id);
x = fa[top[x]];
}
if (dep[x] < dep[y]) swap(x,y);
tmp = merge(tmp,query(dfn[y],dfn[x]));
if (tmp == -1) res = 0;
modify(dfn[y],dfn[x],id);
return res;
}
struct data {
int u,v,len;
bool operator < (const data& x) const {
return len > x.len;
}
} dat[N];
int main() {
int a,b,c;
scanf("%d%d",&n,&m);
for (int i = 1 ; i < n ; ++ i) {
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs_init(1);
dfs_build(1,1);
for (int i = 1 ; i <= m ; ++ i) {
scanf("%d%d",&a,&b);
c = lca(a,b);
dat[i] = (data) {a,b,dep[a] + dep[b] - 2 * dep[c]};
}
sort(dat+1,dat+m+1);
ans = 1;
for (int i = 1 ; i <= m ; ++ i)
ans &= doit(dat[i].u,dat[i].v,i);
if (ans) puts("Yes");
else puts("No");
return 0;
}
小结:还有好几道题根本没法补……感觉自己缺少创造性的思维,也许是应该多做这种类型的题目了。
【做题】neerc2017的A、C、I、L的更多相关文章
- l洛谷 P3926 SAC E#1 - 一道不可做题 Jelly
P3926 SAC E#1 - 一道不可做题 Jelly 题目背景 SOL君(炉石主播)和SOL菌(完美信息教室讲师)是好朋友. 题目描述 SOL君很喜欢吃蒟蒻果冻.而SOL菌也很喜欢蒟蒻果冻. 有一 ...
- [日记&做题记录]-Noip2016提高组复赛 倒数十天
写这篇博客的时候有点激动 为了让自己不颓 还是写写日记 存存模板 Nov.8 2016 今天早上买了两个蛋挞 吃了一个 然后就做数论(前天晚上还是想放弃数论 但是昨天被数论虐了 woc noip模拟赛 ...
- SDOI2016 R1做题笔记
SDOI2016 R1做题笔记 经过很久很久的时间,shzr终于做完了SDOI2016一轮的题目. 其实没想到竟然是2016年的题目先做完,因为14年的六个题很早就做了四个了,但是后两个有点开不动.. ...
- AtCoder Grand Contest 11~17 做题小记
原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-11-to-20.html UPD(2018-11-16): ...
- AtCoder Grand Contest 1~10 做题小记
原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-1-to-10.html 考虑到博客内容较多,编辑不方便的情 ...
- 【做题】BZOJ2342 双倍回文——马拉车&并查集
题意:有一个长度为\(n\)的字符串,求它最长的子串\(s\)满足\(s\)是长度为4的倍数的回文串,且它的前半部分和后半部分都是回文串. \(n \leq 5 \times 10^5\) 首先,显然 ...
- BZOJ做题记录[0512~?]
觉得做一道开一篇真不好...好多想找的东西都被刷下去了... 至于?的日期究竟到什么时候...还是看心情...但是估计不会超过七天吧 最后更新时间:05/19 10:42 [05/14 10:56]我 ...
- LIS【p1704】寻找最优美做题曲线
Description 洛谷OJ刷题有个有趣的评测功能,就是系统自动绘制出用户的"做题曲线".所谓做题曲线就是一条曲线,或者说是折线,是这样定义的:假设某用户在第b[i]天AC了c ...
- luogu P1704 寻找最优美做题曲线
题目背景 nodgd是一个喜欢写程序的同学,前不久(好像还是有点久了)洛谷OJ横空出世,nodgd同学当然第一时间来到洛谷OJ刷题.于是发生了一系列有趣的事情,他就打算用这些事情来出题恶心大家-- 题 ...
- Sam做题记录
Sam做题记录 Hihocoder 后缀自动机二·重复旋律5 求一个串中本质不同的子串数 显然,答案是 \(\sum len[i]-len[fa[i]]\) Hihocoder 后缀自动机三·重复旋律 ...
随机推荐
- [5]windows内核情景分析---进程线程
本篇主要讲述进程的启动过程.线程的调度与切换.进程挂靠 进程的启动过程: BOOL CreateProcess ( LPCTSTR lpApplicationName, ...
- linux设置时间显示格式和系统版本
[修改显示日期格式] vim /etc/bashrc alias ll='ls -l --time-style="+%Y-%m-%d %H:%M:%S"' alias date=' ...
- clientWidth,offsetWidth,scrollWidth区别
<html> <head> <title>clientWidth,offsetWidth,scrollWidth区别</title> </head ...
- 用Django实现Video页面分类查询
Model表创建,Url映射,Views函数处理,Html生成 根据上图,视频方向与视频分类是多对多的关系,视频分类与视频信息是一对多的关系,难度级别是单一的查询条件(与之前俩者并无关系) Model ...
- Lua 判断表是否为空方法
[1]判断表为空的方法 目前为止,Lua语言中判断table表是否为空有三种方式: (1)#table,当table为数组时直接返回table表的长度. (2)当table是字典时,返回table的长 ...
- jack welch:“你们知道了,但是我们做到了”
一.我们发现,只要我们敢于相信自己,敢于朝着那些看似不可能的目标不懈努力,最终会如愿以偿,个人的领袖形象也将因此而确立. 二.一个领导者必须要有魄力.对我来说,这就是一个人能否领导一项业务的分水岭. ...
- JS实现document.ready
通常我们想要在页面内容加载完成后运行 JS 时,都会使用 window.onload 来处理,比如: window.onload = function(){ alert('Hello World!') ...
- SQL SERVER镜像配置,无法将 ALTER DATABASE 命令发送到远程服务器实例的解决办法
环境:非域环境 因为是自动故障转移,需要加入见证,事务安全模式是,强安全FULL模式 做到最后一步的时候,可能会遇到 执行( ALTER DATABASE [mirrortest] SET WITNE ...
- P1012 拼数
P1012 拼数 输入输出样例 输入样例 3 13 312 343 输出样例 34331213 注意 当你输入: 6321 32 407 135 13 217 应该输出: 40732321217135 ...
- JS3D效果
<!DOCTYPE html> <html> <head> <title></title> <meta charset="u ...