Codeforces Round #549 (Div. 1) 题解
前几天补完了某一场很早以前的div1,突然想来更博客,于是就有了这篇文章
A The Beatles
显然若起点和第一次到达的位置距离为 d ,那么经过的不同站点数为 $\frac{nk}{\gcd(d,nk)}$ 。
假设距离起点最近的快餐店是 1 ,枚举距离第一次到达的位置最近的快餐店是多少, $2^2$ 枚举起点和终点在左还是右,更新答案即可。
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long using namespace std; const int N=1e5+;
int n,m,a,b; ll len,ans1,ans2; ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
inline ll get(ll x){
x<=?x+=len:;
x>len?x-=len:;
return x;
}
inline void upd(ll x){
x=len/gcd(len,x);
ans1=min(ans1,x),ans2=max(ans2,x);
}
inline void chk(ll x,ll y){
if (x>y) swap(x,y);
upd(y-x),upd(x+len-y);
} int main(){
scanf("%d%d%d%d",&n,&m,&a,&b); len=(ll)n*m;
ans1=len+,ans2=;
ll q=;
rep (i,,n){
ll p=(ll)(i-)*m+;
ll x=get(p-a),y=get(q-b); chk(x,y);
x=get(p-a),y=get(q+b); chk(x,y);
x=get(p+a),y=get(q-b); chk(x,y);
x=get(p+a),y=get(q+b); chk(x,y);
}
printf("%lld %lld\n",ans1,ans2);
return ;
}
B Lynyrd Skynyrd
相当于匹配一个环。
根据原排列可以知道每个数的后继 nxt[i] 是多少,那么在新序列里的某个位置 p ,它的下一个能匹配上的位置肯定是 nxt[a[p]] 所在的位置,显然贪心找 p 之后距离 p 最近的那个位置最优。
如果记 jump[i] 表示新序列上 i 位置后的下一个能匹配的位置,若不存在 jump[i]=n+1 。那么使用倍增可以得出 i 位置作为子序列的开头,匹配上的最小的右端点 R[i] 。
那么一个区间 [l,r] 能匹配上当且仅当存在 $i\in[l,r] , R[i]\leq r$ 。维护区间最小值即可。
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define Vi vector<int> using namespace std; const int N=2e5+;
int n,m,Q,a[N],b[N],nxt[N],st[N][],R[N],lg[N]; Vi p[N]; int get_min(int x,int y){
int t=lg[y-x+];
return min(st[x][t],st[y-(<<t)+][t]);
} int main(){
scanf("%d%d%d",&n,&m,&Q);
rep (i,,n) scanf("%d",&a[i]);
rep (i,,n) nxt[a[i]]=a[i%n+];
rep (i,,m) scanf("%d",&b[i]),p[b[i]].push_back(i);
rep (i,,m){
int x=nxt[b[i]];
int t=upper_bound(p[x].begin(),p[x].end(),i)-p[x].begin();
if (t==(int)p[x].size()) st[i][]=m+; else st[i][]=p[x][t];
}
st[m+][]=m+;
rep (j,,) rep (i,,m+) st[i][j]=st[st[i][j-]][j-];
rep (i,,m){
int x=i;
for (int j=;~j;j--) if ((n-)>>j&) x=st[x][j];
R[i]=x;
}
memset(st,,sizeof(st));
rep (i,,m) st[i][]=R[i],lg[i]=i==?:lg[i>>]+;
rep (j,,)
for (int i=;i+(<<(j-))<=m;i++)
st[i][j]=min(st[i][j-],st[i+(<<(j-))][j-]);
while (Q--){
int l,r; scanf("%d%d",&l,&r);
if (get_min(l,r)<=r) putchar(''); else putchar('');
}
return ;
}
C U2
注意到一个抛物线 $y=x^2+bx+c$ 可以写成 $y-x^2=bx+c$ ,如果把平面上所有点坐标变为 $(x,y-x^2)$ ,那么就是 $y=bx+c$ ,一条直线。
一条抛物线的上侧部分也就是 $y>x^2+bx+c$ 的那部分,也就是坐标变换后的 $y>bx+c$ 。
原来的问题变为:两两点对连成的直线中,有多少直线的上部没有任何点。即求上凸壳的边数。
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long using namespace std; const int N=1e5+;
int n,top; #define vec poi
struct poi{
ll x,y;
poi(ll x=,ll y=):x(x),y(y){}
friend vec operator - (vec A,vec B){return vec(A.x-B.x,A.y-B.y);}
friend ll operator ^ (vec A,vec B){return A.x*B.y-A.y*B.x;}
friend bool operator < (poi x,poi y){return x.x!=y.x?x.x<y.x:x.y<y.y;}
}a[N],stk[N]; int main(){
scanf("%d",&n);
rep (i,,n){
scanf("%lld%lld",&a[i].x,&a[i].y);
a[i].y-=a[i].x*a[i].x; }
sort(a+,a++n);
rep (i,,n){
while (top>=&&((a[i]-stk[top-])^(stk[top]-stk[top-]))<=) top--;
while (top&&a[i].x==stk[top].x) top--;
stk[++top]=a[i];
}
printf("%d\n",top-);
return ;
}
D Foreigner
看了别人的题解,学到了 $\mathcal{O}(11n)$ 的 dp 姿势。
这个序列显然是有一定规律性的。大致是这样:1,2,3,4,...,9, 10, 20,21, 30,31,32, 40,41,42,43, ..., 100,101,...,109, , 210, 300,301, ...
其实就是从 10 开始,每个数都是从 1 开始的某个序列中的数往后添加 0,1,...,9 ,并且能添上的数字个数是 0...10 循环的。
考虑求一个合法的数的 rank :
若当前数位num,记 x 为 rk[num] , y 为 num 去掉末位的 rank ,那么有 $x=9 + 55\lfloor \frac {y}{10} \rfloor + 0+1+...+y\%11-1 + num\%10+1$ 。
由于 $11|55$ ,所以 $x\%11=9 + 0+1+...+y\%11-1 + num\%10+1$ 。发现我们只需要知道去掉末位的数的 rk%11 的值即可,就可以推出加上一个数后的 rk%11的值了。
用 f[i][j] 表示现在在 i 位置, i 之前的数%11的值为 j ,可以延伸到的最右端点。记忆化搜索更加简洁易懂。
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long using namespace std; const int N=1e5+;
int n,f[N][]; ll ans; char s[N]; int dfs(int x,int y){
if (x>n) return x-;
if (~f[x][y]) return f[x][y];
if (s[x]>=y) return x-;
return f[x][y]=dfs(x+,(y*(y-)/++s[x]%)%);
} int main(){
memset(f,-,sizeof(f));
scanf("%s",s+),n=strlen(s+);
rep (i,,n) s[i]-='';
rep (i,,n) if (s[i]) ans+=dfs(i+,s[i])-i+;
printf("%lld\n",ans);
return ;
}
E Pink Floyd
感觉挺神的
首先考虑没有粉色边的情况:
随便找一个点 x ,先假设 x 是答案。然后再找一个 x 无法到达的点 y ,若 $x\rightarrow y$ ,那么标记 y 为可达,否则将 y 设为答案, x 标记为可达。$\mathcal{O}(n)$ 次询问后会标记完所有的点,输出此时的答案即可。其实相当于一个不停转移答案节点的过程,感觉非常妙~
如果有了粉色的话,首先缩点,然后按照拓扑序:
先随便找一个度为 0 的 scc ,在里面随便找一个点 x 设为答案;然后找另一个还存在的度为 0 的 scc ,在里面随便找一个点 y ,询问 (x,y)。此时若 $x\rightarrow y$ ,把 y 删掉(和上面标记可达的意思差不多);否则将答案转移给 y ,把 x 删掉。注意这里如果一个 scc 里的点被删完了,就要把整个 scc 及它的出边删掉,加进来新的入度为 0 的 scc (类似 toposort )。
胡一下为什么先删度数为 0 的 scc :(很显然吗?)
原因是由于粉色边的存在,我们需要利用已有的粉色边。考虑一个状态,我们找到了 $u\rightarrow v$ ,即 $u\rightarrow v$ 无粉色路径有绿色边,但已知 $v\rightarrow u$ 有粉色路径。这个时候按照我们的算法会把 v 抛弃,将答案转移到 u 上来,但是事实上 v 有可能成为最终答案,如果擅自转移到 u 可能最终无法得出答案,找不到 v 。但是如果按照拓扑序来更新,每次都取度为拓扑序最靠前的 scc ,这样就不会出现 $u\rightarrow v$ 无粉色路径有绿色边, $v\rightarrow u$ 有粉色路径的情况,所以每次转移都是对的。
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define Vi vector<int> using namespace std; const int N=1e5+;
int n,m,x[N],y[N],cnt,head[N];
int dfn[N],low[N],stk[N],bel[N],top,clk,scc,rd[N]; Vi V[N];
int q[N],Head[N]; set<int> S[N];
struct edge{int to,nxt;}e[N<<]; void adde(int *head,int x,int y){
e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt;
} void tarjan(int u){
dfn[u]=low[u]=++clk; stk[++top]=u;
for (int i=head[u],v;i;i=e[i].nxt)
if (v=e[i].to,!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
} else if (!bel[v]) low[u]=min(low[u],dfn[v]);
if (dfn[u]!=low[u]) return;
++scc;
while (stk[top]!=u) V[scc].push_back(stk[top]),bel[stk[top--]]=scc;
V[scc].push_back(stk[top]),bel[stk[top--]]=scc;
} int ask(int x,int y){
printf("? %d %d\n",x,y),fflush(stdout);
scanf("%d",&x); return x;
} int main(){
scanf("%d%d",&n,&m);
rep (i,,m) scanf("%d%d",&x[i],&y[i]),adde(head,x[i],y[i]);
rep (i,,n) if (!dfn[i]) tarjan(i);
rep (i,,m)
if (bel[x[i]]!=bel[y[i]]&&!S[bel[x[i]]].count(bel[y[i]])){
adde(Head,bel[x[i]],bel[y[i]]);
rd[bel[y[i]]]++,S[bel[x[i]]].insert(bel[y[i]]);
}
int tail=;
rep (i,,scc) if (!rd[i]) q[++tail]=i;
while (tail>){
int x=V[q[]].back(),y=V[q[]].back();
if (!ask(x,y)) swap(q[],q[]);
x=q[]; V[x].pop_back();
if (V[x].empty()){
swap(q[tail],q[]),tail--;
for (int i=Head[x],v;i;i=e[i].nxt)
if (v=e[i].to,!--rd[v]) q[++tail]=v;
}
}
printf("! %d\n",V[q[]].back()),fflush(stdout);
return ;
}
Codeforces Round #549 (Div. 1) 题解的更多相关文章
- [题解] Codeforces Round #549 (Div. 2) B. Nirvana
Codeforces Round #549 (Div. 2) B. Nirvana [题目描述] B. Nirvana time limit per test1 second memory limit ...
- Codeforces Round #549 (Div. 1)
今天试图用typora写题解 真开心 参考 你会发现有很多都是参考的..zblzbl Codeforces Round #549 (Div. 1) 最近脑子不行啦 需要cf来缓解一下 A. The B ...
- Codeforces Round #182 (Div. 1)题解【ABCD】
Codeforces Round #182 (Div. 1)题解 A题:Yaroslav and Sequence1 题意: 给你\(2*n+1\)个元素,你每次可以进行无数种操作,每次操作必须选择其 ...
- Codeforces Round #608 (Div. 2) 题解
目录 Codeforces Round #608 (Div. 2) 题解 前言 A. Suits 题意 做法 程序 B. Blocks 题意 做法 程序 C. Shawarma Tent 题意 做法 ...
- Codeforces Round #525 (Div. 2)题解
Codeforces Round #525 (Div. 2)题解 题解 CF1088A [Ehab and another construction problem] 依据题意枚举即可 # inclu ...
- Codeforces Round #528 (Div. 2)题解
Codeforces Round #528 (Div. 2)题解 A. Right-Left Cipher 很明显这道题按题意逆序解码即可 Code: # include <bits/stdc+ ...
- Codeforces Round #466 (Div. 2) 题解940A 940B 940C 940D 940E 940F
Codeforces Round #466 (Div. 2) 题解 A.Points on the line 题目大意: 给你一个数列,定义数列的权值为最大值减去最小值,问最少删除几个数,使得数列的权 ...
- Codeforces Round #677 (Div. 3) 题解
Codeforces Round #677 (Div. 3) 题解 A. Boring Apartments 题目 题解 简单签到题,直接数,小于这个数的\(+10\). 代码 #include &l ...
- Codeforces Round #665 (Div. 2) 题解
Codeforces Round #665 (Div. 2) 题解 写得有点晚了,估计都官方题解看完切掉了,没人看我的了qaq. 目录 Codeforces Round #665 (Div. 2) 题 ...
随机推荐
- Python3.x文件处理详解
Python3.x文件处理详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 任何一门语言都有其特有的操作文件的方式,Python2.x版本有两种操作文件的方式,没错就是open函 ...
- 一些常见修改命令(针对ubuntu 14.04 持续更新中...)
1.PS1 在哪: echo $PS1 vi /etc/bash.bashrc /W输出最后一个目录 /w输出完整目录 2.设置静态IP地址:vim /etc/network/i ...
- mongodb与mysql的区别与具体应用场景
MongoDB: 非关系型数据库,文档型数据库, 文档型数据库:可以存放xml,json,bson类型的数据.这些数据具备自述性(self-describing),呈现分层的树状数据结构.数据结构由键 ...
- MYSQL——root密码更换
方法1: 用SET PASSWORD命令mysql -u rootmysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('newpass') ...
- python学习笔记7-excel操作
一.操作excel import xlwt book = xlwt.Workbook() #新建一个excel sheet = book.add_sheet('sheet1') #添加一个sheet页 ...
- CSS规范 - 分类方法--(来自网易)
CSS文件的分类和引用顺序 通常,一个项目我们只引用一个CSS,但是对于较大的项目,我们需要把CSS文件进行分类. 我们按照CSS的性质和用途,将CSS文件分成“公共型样式”.“特殊型样式”.“皮肤型 ...
- 基于序列化技术(Protobuf)的socket文件传输
好像好久都没更博文了,没办法,最近各种倒霉事情,搞到最近真的没什么心情,希望之后能够转运吧. 言归正传,这次我要做的是基于序列化技术的socket文件传输来无聊练一下手. 一.socket文件传输 之 ...
- J2EE简介
一,J2EE概念: J2EE的全称为,Java2 Platform Enterprise Edition,Java或java2平台企业版,他是基于java平台或java2平台的标准版,保留并扩展了J2 ...
- java Concurrent 中的数据结构
一:阻塞数据结构(线程安全) ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列. LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列. PriorityBl ...
- 20155314 2016-2017-2 《Java程序设计》第5周学习总结
20155314 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 理解异常架构 牚握try...catch...finally处理异常的方法 会用throw, ...