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的更多相关文章

  1. l洛谷 P3926 SAC E#1 - 一道不可做题 Jelly

    P3926 SAC E#1 - 一道不可做题 Jelly 题目背景 SOL君(炉石主播)和SOL菌(完美信息教室讲师)是好朋友. 题目描述 SOL君很喜欢吃蒟蒻果冻.而SOL菌也很喜欢蒟蒻果冻. 有一 ...

  2. [日记&做题记录]-Noip2016提高组复赛 倒数十天

    写这篇博客的时候有点激动 为了让自己不颓 还是写写日记 存存模板 Nov.8 2016 今天早上买了两个蛋挞 吃了一个 然后就做数论(前天晚上还是想放弃数论 但是昨天被数论虐了 woc noip模拟赛 ...

  3. SDOI2016 R1做题笔记

    SDOI2016 R1做题笔记 经过很久很久的时间,shzr终于做完了SDOI2016一轮的题目. 其实没想到竟然是2016年的题目先做完,因为14年的六个题很早就做了四个了,但是后两个有点开不动.. ...

  4. AtCoder Grand Contest 11~17 做题小记

    原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-11-to-20.html UPD(2018-11-16): ...

  5. AtCoder Grand Contest 1~10 做题小记

    原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-1-to-10.html 考虑到博客内容较多,编辑不方便的情 ...

  6. 【做题】BZOJ2342 双倍回文——马拉车&并查集

    题意:有一个长度为\(n\)的字符串,求它最长的子串\(s\)满足\(s\)是长度为4的倍数的回文串,且它的前半部分和后半部分都是回文串. \(n \leq 5 \times 10^5\) 首先,显然 ...

  7. BZOJ做题记录[0512~?]

    觉得做一道开一篇真不好...好多想找的东西都被刷下去了... 至于?的日期究竟到什么时候...还是看心情...但是估计不会超过七天吧 最后更新时间:05/19 10:42 [05/14 10:56]我 ...

  8. LIS【p1704】寻找最优美做题曲线

    Description 洛谷OJ刷题有个有趣的评测功能,就是系统自动绘制出用户的"做题曲线".所谓做题曲线就是一条曲线,或者说是折线,是这样定义的:假设某用户在第b[i]天AC了c ...

  9. luogu P1704 寻找最优美做题曲线

    题目背景 nodgd是一个喜欢写程序的同学,前不久(好像还是有点久了)洛谷OJ横空出世,nodgd同学当然第一时间来到洛谷OJ刷题.于是发生了一系列有趣的事情,他就打算用这些事情来出题恶心大家-- 题 ...

  10. Sam做题记录

    Sam做题记录 Hihocoder 后缀自动机二·重复旋律5 求一个串中本质不同的子串数 显然,答案是 \(\sum len[i]-len[fa[i]]\) Hihocoder 后缀自动机三·重复旋律 ...

随机推荐

  1. 软工网络15团队作业4——Alpha阶段敏捷冲刺4.0

    软工网络15团队作业4--Alpha阶段敏捷冲刺4.0 1.每天举行站立式会议,提供当天站立式会议照片一张. 2.项目每个成员的昨天进展.存在问题.今天安排. 成员 昨天已完成 今天计划完成 郭炜埕 ...

  2. JSP中的编码问题

    JSP文件的编码 <%@ page contentType="text/html;charset=UTF-8" language="java" %> ...

  3. Python全栈-day10-函数2

    函数高级篇 1.函数嵌套 1)嵌套定义 在函数内定义另外一个函数 def func(): print('嵌套定义') def func1(): print('这是一个嵌套函数') def func2( ...

  4. Rpgmakermv(15) PH任务插件

    插件介绍 一个用来简单显示任务阶段的任务书 使用方法 插件安装 下载js文件放置到游戏目录/plugins目录下.打开插件管理器,选择PH_QuestBook.js并开启. 插件参数 Show in ...

  5. 32网络通信之Poll模型

    多路复用并发模型  -- poll #include<poll.h> int  poll(struct pollfd *fds,  unsigned int nfds, int timeo ...

  6. IoC, DI,Spring.net

    IoC : Inversion of Control , 控制反转,就是创建对象(实例)的权利由开发人员自己控制New转到了由容器来控制.实现了解耦. DI: Dependency Injection ...

  7. Log4J基础详解及示例大全(转)

    log4j可以通过使用配置文件的方式进行配置. 配置步骤如下: 1.定义日志组件logger 每个logger都可以拥有一个或者多个appender,每个appender表示一个日志的输出目的地,比如 ...

  8. 分享30道Redis面试题,面试官能问到的我都找到了

    1.什么是Redis?简述它的优缺点? Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到 ...

  9. 如何通过命令行使用Wisdom RESTClient?

    Wisdom RESTClient V1.2版本开始支持命令行方式运行. 工具地址: https://github.com/Wisdom-Projects/rest-client 使用说明:java ...

  10. 计算概论(A)/基础编程练习1(8题)/1:大象喝水

    #include<stdio.h> int main() { ; // n < 100 scanf("%d", &n); // 循环遍历判断 再进行平方和 ...