Codeforces Round #397 题解
Problem A. Neverending competitions
题目大意
一个团队有多个比赛,每次去比赛都会先订机票去比赛地点,然后再订机票返回.给出\(n\)个含有起止地点的购票记录(不按时间顺序),判断这个团队是否返回.
题解
竞速题
因为每次比赛都必定是一去一返
所以当订票记录个数为偶数的时候一定在家中,否则一定在比赛.
Code
a = input()
if a % 2 == 0:
print 'home'
else:
print 'contest'
Problem B. Code obfuscation
题目大意
在一个不同的词数目不超过26种且单个词长度>1的文档中,做如下替换:从前到后找出第一个长度>1的单词,并把所有这种单词替换成a,然后继续找..替换成b,继续..替换成c,继续...直到不存在这样的单词位置.然后删去所有的不可见字符构成一个字符串
现在给定一个字符串,判断这个字符串有没有可能是由上述替换得到的.
\(len \leq 500\)
题解
这个题面很长也不要怪我啊...
我们发现这样得到的字符串一定满足这样的一个性质:
若字母\(ch\)出现,那么\('a' .. (ch-1)\)一定都在之前就出现过了
所以我们应用这个性质直接判断即可.
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
char s[512];
int main(){
scanf("%s",s+1);
int n = strlen(s+1);
int nw = 'a' - 1;
for(int i=1;i<=n;++i){
if(s[i] == nw+1) ++ nw;
if(s[i] > nw) return puts("NO");
}
puts("YES");
return 0;
}
Problem C. Table Tennis Game 2
题目大意
一场比赛结束当且仅当某一方的得分恰好为\(k\),现在给出若干场比赛后小M和小V的各场比赛得分之和,判断是否合法.若合法,则输出可能的最多比赛的
场数.\(k,a,b \leq 10^9\)
题解
设得分分别为\(sco_a,sco_b\)
我们知道合法时最多可能的比赛场数一定为\(\frac{sco_a}{k} + \frac{sco_b}{k}\)
那么现在问题就在于判定是否合法
我们知道如果一个局面不合法,一定是有某一方有余出来的无法解释来由的得分
例如\(k = 2,sco_1 = 1,sco_2 = 1\)
双方都无法解释得分的来由
但是如果有\(k = 2,sco_1 = 3,sco_2 = 1\)
\(sco_2\)就可以解释的通了
所以我们可以做如下判定
if( ( a < k && b % k) || (b < k && a % k) ) puts("-1");
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
int main(){
int k,a,b;read(k);read(a);read(b);
if( ( a < k && b % k) || (b < k && a % k) ) puts("-1");
else printf("%d\n",a/k + b/k);
return 0;
}
Problem D. Artsem and Saunders
题目大意
定义\([n]\)表示集合\({1,2,..,n}\)给定函数\(f:[x]->[y]\)要求构造函数:\(g:[n]->[m]\)和\(h:[m]->[n]\)满足\(g(h(x)) = x,x \in [m]\)且\(h(g(x)) = f(x),x \in [n]\)。存在则输出,不存在输出-1.\((n \leq 10^5,m \leq 10^6)\)
题解
其实这道题就是让我们构建一系列的映射关系
我们发现要求\(g(h(x)) = x\)那么我们知道对于所有存在的\(h(x)\)
一定有\(g(h(x)) = x\),换句话说,如果我们知道了\(h(x)\)那么我们就知道\(g(h(x))\)
但是我们这样就直接确定了\(g(x)\)有可能(并且是大多数情况下)不满足\(g(h(x)) = x\)
我们想,在\(i,f(i)\)是什么情况的时候,有前式直接确定的\(g(x)\)满足后式
只有当\(i = f(i)\)的时候,我们可以令\(h(m) = i,g(i) = m\)
那么当\(i != f(i)\)呢,如果\(f(f(i)) = f(i)\)也就是我们映射到的元素所发出的映射已经被\(h(x),g(x)\)映射过了
那么我们就加一条\(g(i) = g(f(i))\)的映射就依然满足条件了。
如果\(f(f(i)) != f(i)\)那就无论如何都不可能有解了。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 1000010;
int f[maxn],g[maxn],h[maxn];
int main(){
int n;read(n);
int m = 0;
for(int i=1;i<=n;++i){
read(f[i]);
if(f[i] == i){
h[++m] = i;
g[i] = m;
}
}
for(int i=1;i<=n;++i){
if(i != f[i]){
if(f[f[i]] != f[i]) return puts("-1");
else g[i] = g[f[i]];
}
}
printf("%d\n",m);
for(int i=1;i<=n;++i){
printf("%d",g[i]);
if(i != n) putchar(' ');
else putchar('\n');
}
for(int i=1;i<=m;++i){
printf("%d",h[i]);
if(i != m) putchar(' ');
else putchar('\n');
}
return 0;
}
Problem E. Tree Folding
题目大意
定义一种在树上的操作:选中树上的一条点数为奇数路径,取路径的中点,使左右两条路径剩余的点不含有除路径上的点的邻居。此时可以去掉两条路径中的一条路径(中点保留).问最后能否把这个树化成一条链.若能输出链的最小边数。\((n \leq 10^5)\)
题解
题目是个谜
读了半天才读懂。。。
其实就是选择一个点,然后若这个点的两个子树是长度相同的链
就可以消掉两个子树中的一个
我们发现所有长度相同的是链的子树一定能合并
而且这道题很好dp,于是我们使用树形dp来解决这个问题
自底向上更新时,首先我们知道所有的子树都必须转化成链,这样答案才有可能
转化成链的充要条件是子树的链只有一种长度,因为有两种长度及以上就要分叉了
不过如果只有两种长度的话,那么这个点作为根也是可能可行的,于是我们需要找到一个这样的点尝试使其作为根.
而如果有两种以上的长度的话就不用想了,问题无解。
如果只有一种长度的话我们就直接将长度+1然后向上更新即可
对于长度的判重本来写了个Hash_map的,结果炸了,改成了set..
Code
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 200010;
struct Edge{
int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
inline void insert(int u,int v){
add(u,v);add(v,u);
}
int rt = 1,rec;
bool flag = false;
#define v G[i].to
int dfs(int u,int f){
set<int>s;
for(int i = head[u];i;i=G[i].next){
if(v == f) continue;
int x = dfs(v,u);
if(x == -1) return -1;
s.insert(x+1);
}
if( (s.size() == 2 && f) ){rt = u;return -1;}
if( s.size() > 2 ){flag = true;return -1;}
if(s.size() == 0) return 0;
set<int>::iterator it = s.begin();
int x = *it;
if(s.size() == 1) return x;
return x + (*(++it));
}
#undef v
int main(){
int n;read(n);
for(int i=1,u,v;i<n;++i){
read(u);read(v);
insert(u,v);
}
int ans = dfs(1,0);
if(flag) return puts("-1");
flag = false;rec = rt;
if(rt != 1) ans = dfs(rt,0);
if(flag || rec != rt) return puts("-1");
while(ans && !(ans&1)) ans >>= 1;
printf("%d\n",ans);
getchar();getchar();
return 0;
}
Problem F. Souvenirs
题目大意
给定一个长为\(n\)的序列,每次询问区间\([l,r]\)内的任意两个元素的最小的_差值的绝对值_.\((n \leq 10^5,m \leq 3*10^5)\)
题解
这道题我想了不少做法.
algorithm 1:
使用一颗线段树来维护区间内最小元素差
向上更新时使用归并排序的方式合并.(这好像叫做划分树)
预处理没有问题,问题在于查询的时候无法简单合并区间...
所以在查询的时候考虑使用std::set来解决这个东西
利用set进行启发式合并可以做到\(O(mlog^3n)\)
做点底层优化应该能过@WC2017的某松
algorithm 2:
我们仍然使用一颗线段树来维护区间内的最小元素差
但是我们先不统计出所有的答案
只在线段树的节点内存上某个点代表的区间所包含的所有元素
我们将所有的询问按第一关键字为l,第二关键字为r排序
然后我们从序列右端点开始向左扫描
依次考虑每个点对这个点到序列端点的区间所造成的影响
每次处理影响我们就类比于线段树的区间修改操作
修改线段树上每个节点内存储的最小元素差
但是这样我们就发现:现在线段树中的点已经不满足初始的定义了
因为对于所有的线段树中的点所代表的左区间其实都变成了当前扫描线所在的位置
这恰恰是我们对于询问排序的目的。
我们发现,在这种顺序下的询问中,上述错误不会带来任何影响
只要我们在扫描线扫描到某一个点的左端点的时候立即查询即可
因为右边界仍是满足的。
然后你需要一个set来剪枝加速,不然会Time limit exceeded on test 7
Code
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
const int maxm = 300010;
int minn[maxn<<2];
int tmp[maxn],a[maxn];
int L,R,val,nw_min,n;
set<int>T[maxn<<2];
void build(int rt,int l,int r){
minn[rt] = 0x7f7f7f7f;
for(int i=l;i<=r;++i) T[rt].insert(a[i]);
if(l == r) return;
int mid = l+r >> 1;
build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
}
int query(int rt,int l,int r){
if(L <= l && r <= R) return minn[rt];
int mid = l+r >> 1;
if(R <= mid) return query(rt<<1,l,mid);
if(L > mid) return query(rt<<1|1,mid+1,r);
return min(query(rt<<1,l,mid),query(rt<<1|1,mid+1,r));
}
inline bool exit_it(int rt){
set<int>::iterator it = T[rt].lower_bound(val);
int y = it == T[rt].end() ? -1 : *it;
int x = -1;
if(it != T[rt].begin()) x = *(--it);
if( (y == -1 || abs(y-val) >= nw_min) && (x == -1 || (abs(x-val) >= nw_min) ) ){
nw_min = min(nw_min,minn[rt]);
return true;
}return false;
}
void cacu(int rt,int l,int r){
if(l == r){
minn[rt] = min(minn[rt],abs(val - a[l]));
nw_min = min(nw_min,minn[rt]);
return ;
}
if(exit_it(rt)) return;
int mid = l+r >> 1;
if(L <= mid) cacu(rt<<1,l,mid);
if(R > mid) cacu(rt<<1|1,mid+1,r);
minn[rt] = min(minn[rt<<1],minn[rt<<1|1]);
}
struct Node{
int l,r,id;
bool friend operator < (const Node &a,const Node &b){
return !(a.l == b.l ? a.r < b.r : a.l < b.l );
}
}q[maxm];
int anss[maxm];
int main(){
read(n);
for(int i=1;i<=n;++i) read(a[i]);
int m;read(m);
build(1,1,n);
for(int i=1;i<=m;++i){
read(q[i].l);read(q[i].r);
q[i].id = i;
}sort(q+1,q+m+1);
for(int i=1,p=n;i<=m;++i){
while(p >= q[i].l){
L = p+1;R = n;val = a[p];nw_min = 0x7f7f7f7f;
if(L <= R) cacu(1,1,n);
--p;
}
L = q[i].l;R = q[i].r;
anss[q[i].id] = query(1,1,n);
}
for(int i=1;i<=m;++i) printf("%d\n",anss[i]);
getchar();getchar();
return 0;
}
Problem G. Math, math everywhere
这道题实在不会...
orz做出来的大爷们
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
int main(){
while(1) printf("orz\n");
getchar();getchar();
return 0;
}
Codeforces Round #397 题解的更多相关文章
- Codeforces Round #556 题解
Codeforces Round #556 题解 Div.2 A Stock Arbitraging 傻逼题 Div.2 B Tiling Challenge 傻逼题 Div.1 A Prefix S ...
- Codeforces Round #569 题解
Codeforces Round #569 题解 CF1179A Valeriy and Deque 有一个双端队列,每次取队首两个值,将较小值移动到队尾,较大值位置不变.多组询问求第\(m\)次操作 ...
- Codeforces Round #557 题解【更完了】
Codeforces Round #557 题解 掉分快乐 CF1161A Hide and Seek Alice和Bob在玩捉♂迷♂藏,有\(n\)个格子,Bob会检查\(k\)次,第\(i\)次检 ...
- CFEducational Codeforces Round 66题解报告
CFEducational Codeforces Round 66题解报告 感觉丧失了唯一一次能在CF上超过wqy的机会QAQ A 不管 B 不能直接累计乘法打\(tag\),要直接跳 C 考虑二分第 ...
- Codeforces Round #542 题解
Codeforces Round #542 abstract I决策中的独立性, II联通块染色板子 IIIVoronoi diagram O(N^2 logN) VI环上距离分类讨论加取模,最值中的 ...
- Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined) F. Souvenirs 线段树套set
F. Souvenirs 题目连接: http://codeforces.com/contest/765/problem/F Description Artsem is on vacation and ...
- Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined) E. Tree Folding 拓扑排序
E. Tree Folding 题目连接: http://codeforces.com/contest/765/problem/E Description Vanya wants to minimiz ...
- Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined) D. Artsem and Saunders 数学 构造
D. Artsem and Saunders 题目连接: http://codeforces.com/contest/765/problem/D Description Artsem has a fr ...
- Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined) C. Table Tennis Game 2 水题
C. Table Tennis Game 2 题目连接: http://codeforces.com/contest/765/problem/C Description Misha and Vanya ...
随机推荐
- 开发ActiveX控件调用另一个ActiveX系列3——ActiveX调用另一个ActiveX
终于进入正题了,怎样在ActiveX中调用另一个ActiveX.我们的项目需要调用华视电子身份证识别仪的ActiveX控件 在这里有很多识别仪ActiveX插件下载:http://www.idukaq ...
- Spring IOC源码分析之-刷新前的准备工作
目录 ClassPathXmlApplicationContext的注册方式 加载父子容器 配置路径解析 容器刷新 刷新容器之刷新预处理 ClassPathXmlApplicationContext的 ...
- MagicalRecord使用教程【转载】
原文地址:http://www.ithao123.cn/content-96403.html 下面是在xcode5.1下ARC环境中的使用教程 1. 将 MagicalRecord 文件夹拖入到工程文 ...
- Selenium3 Python3 Web自动化测试从基础到项目实战之一启动不同的浏览器及配置
在web自动化中目前selenium作为底层的自动化测试是目前运用最广的,但是各个公司都会在这个基础之上进行修改.从今天开始我们就慢慢从low代码一步一步的学习框架知识. 首先当我们测试环境有了之后我 ...
- Ejabberd作为推送服务的优化手段(转)
AVOS Cloud目前还在用Ejabberd做Android的消息推送服务.当时选择Ejabberd,是因为Ejabberd是一个发展很长时间的XMPP实现,并且基于Erlang,设想能在我们自主研 ...
- 多媒体开之之rtp 时间戳和负载类型介绍
(1)时间戳 (2)负载类型 (3)rtp 包头 (1)时间戳 有三个 一个实时间单位 timestamp_increse=(unsigned int)(90000.0 / framerate); / ...
- Android 事件分发机制 图解
在Android 开发中事件分发是比较重要的,也是比较难理解的,之前看过这方面的东西,以为自己弄懂了,也就没太注意,最近面试呢,想着肯定要问到这一块的东西,回顾的时候发现又忘了,真是好记性不如烂笔头啊 ...
- 关于EasyRTSPClient、EasyPlayer RTSP流重连问题的解释
EasyPlayer.EasyRTSPClient是如何设计重连的 首先大概解释一下EasyRTSPClient与EasyPlayer间的关系:EasyRTSPClient是一个专门用于与RTSP流媒 ...
- java中随机生成汉字
main方法中使用: //随机生成100个汉字 String ss=""; for(int i=0;i<100;i++){ ss+=getChinese(i); } Syst ...
- c#数组的count()和length的区别
C# 数组中 Length 表示数组项的个数,是个属性. 而 Count() 也是表示项的个数,是个方法,它的值和 Length 一样.但实际上严格地说 Count() 不是数组的内容,而是 IEnu ...