zoukankan      html  css  js  c++  java
  • Codeforces Round #549 (Div. 1) 题解

    link

    前几天补完了某一场很早以前的div1,突然想来更博客,于是就有了这篇文章

    A The Beatles

    显然若起点和第一次到达的位置距离为 d ,那么经过的不同站点数为 $frac{nk}{gcd(d,nk)}$ 。

    假设距离起点最近的快餐店是 1 ,枚举距离第一次到达的位置最近的快餐店是多少, $2^2$ 枚举起点和终点在左还是右,更新答案即可。

     1 #include<bits/stdc++.h>
     2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
     3 #define ll long long
     4 
     5 using namespace std;
     6 
     7 const int N=1e5+10;
     8 int n,m,a,b; ll len,ans1,ans2;
     9 
    10 ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
    11 inline ll get(ll x){
    12     x<=0?x+=len:0;
    13     x>len?x-=len:0;
    14     return x;
    15 }
    16 inline void upd(ll x){
    17     x=len/gcd(len,x);
    18     ans1=min(ans1,x),ans2=max(ans2,x);
    19 }
    20 inline void chk(ll x,ll y){
    21     if (x>y) swap(x,y);
    22     upd(y-x),upd(x+len-y);
    23 }
    24 
    25 int main(){
    26     scanf("%d%d%d%d",&n,&m,&a,&b); len=(ll)n*m;
    27     ans1=len+1,ans2=0;
    28     ll q=1;
    29     rep (i,1,n){
    30         ll p=(ll)(i-1)*m+1;
    31         ll x=get(p-a),y=get(q-b); chk(x,y);
    32         x=get(p-a),y=get(q+b); chk(x,y);
    33         x=get(p+a),y=get(q-b); chk(x,y);
    34         x=get(p+a),y=get(q+b); chk(x,y);
    35     }
    36     printf("%lld %lld
    ",ans1,ans2);
    37     return 0;
    38 }
    View Code

    B Lynyrd Skynyrd

    相当于匹配一个环。

    根据原排列可以知道每个数的后继 nxt[i] 是多少,那么在新序列里的某个位置 p ,它的下一个能匹配上的位置肯定是 nxt[a[p]] 所在的位置,显然贪心找 p 之后距离 p 最近的那个位置最优。

    如果记 jump[i] 表示新序列上 i 位置后的下一个能匹配的位置,若不存在 jump[i]=n+1 。那么使用倍增可以得出 i 位置作为子序列的开头,匹配上的最小的右端点 R[i] 。

    那么一个区间 [l,r] 能匹配上当且仅当存在 $iin[l,r] , R[i]leq r$ 。维护区间最小值即可。

     1 #include<bits/stdc++.h>
     2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
     3 #define Vi vector<int>
     4 
     5 using namespace std;
     6 
     7 const int N=2e5+10;
     8 int n,m,Q,a[N],b[N],nxt[N],st[N][20],R[N],lg[N]; Vi p[N];
     9 
    10 int get_min(int x,int y){
    11     int t=lg[y-x+1];
    12     return min(st[x][t],st[y-(1<<t)+1][t]);
    13 }
    14 
    15 int main(){
    16     scanf("%d%d%d",&n,&m,&Q);
    17     rep (i,1,n) scanf("%d",&a[i]);
    18     rep (i,1,n) nxt[a[i]]=a[i%n+1];
    19     rep (i,1,m) scanf("%d",&b[i]),p[b[i]].push_back(i);
    20     rep (i,1,m){
    21         int x=nxt[b[i]];
    22         int t=upper_bound(p[x].begin(),p[x].end(),i)-p[x].begin();
    23         if (t==(int)p[x].size()) st[i][0]=m+1; else st[i][0]=p[x][t];
    24     }
    25     st[m+1][0]=m+1;
    26     rep (j,1,18) rep (i,1,m+1) st[i][j]=st[st[i][j-1]][j-1];
    27     rep (i,1,m){
    28         int x=i;
    29         for (int j=18;~j;j--) if ((n-1)>>j&1) x=st[x][j];
    30         R[i]=x;
    31     }
    32     memset(st,0,sizeof(st));
    33     rep (i,1,m) st[i][0]=R[i],lg[i]=i==1?0:lg[i>>1]+1;
    34     rep (j,1,18)
    35         for (int i=1;i+(1<<(j-1))<=m;i++)
    36             st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    37     while (Q--){
    38         int l,r; scanf("%d%d",&l,&r);
    39         if (get_min(l,r)<=r) putchar('1'); else putchar('0');
    40     }
    41     return 0;
    42 }
    View Code

    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$ 。

    原来的问题变为:两两点对连成的直线中,有多少直线的上部没有任何点。即求上凸壳的边数。

     1 #include<bits/stdc++.h>
     2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
     3 #define ll long long
     4 
     5 using namespace std;
     6 
     7 const int N=1e5+10;
     8 int n,top;
     9 
    10 #define vec poi
    11 struct poi{
    12     ll x,y;
    13     poi(ll x=0,ll y=0):x(x),y(y){}
    14     friend vec operator - (vec A,vec B){return vec(A.x-B.x,A.y-B.y);}
    15     friend ll operator ^ (vec A,vec B){return A.x*B.y-A.y*B.x;}
    16     friend bool operator < (poi x,poi y){return x.x!=y.x?x.x<y.x:x.y<y.y;}
    17 }a[N],stk[N];
    18 
    19 int main(){
    20     scanf("%d",&n);
    21     rep (i,1,n){
    22         scanf("%lld%lld",&a[i].x,&a[i].y);
    23         a[i].y-=a[i].x*a[i].x;
    24         
    25     }
    26     sort(a+1,a+1+n);
    27     rep (i,1,n){
    28         while (top>=2&&((a[i]-stk[top-1])^(stk[top]-stk[top-1]))<=0) top--;
    29         while (top&&a[i].x==stk[top].x) top--;
    30         stk[++top]=a[i];
    31     }
    32     printf("%d
    ",top-1);
    33     return 0;
    34 }
    View Code

    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 + 55lfloor frac {y}{10} floor + 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 ,可以延伸到的最右端点。记忆化搜索更加简洁易懂。

     1 #include<bits/stdc++.h>
     2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
     3 #define ll long long
     4 
     5 using namespace std;
     6 
     7 const int N=1e5+10;
     8 int n,f[N][13]; ll ans; char s[N];
     9 
    10 int dfs(int x,int y){
    11     if (x>n) return x-1;
    12     if (~f[x][y]) return f[x][y];
    13     if (s[x]>=y) return x-1;
    14     return f[x][y]=dfs(x+1,(y*(y-1)/2+10+s[x]%10)%11);
    15 }
    16 
    17 int main(){
    18     memset(f,-1,sizeof(f));
    19     scanf("%s",s+1),n=strlen(s+1);
    20     rep (i,1,n) s[i]-='0';
    21     rep (i,1,n) if (s[i]) ans+=dfs(i+1,s[i])-i+1;
    22     printf("%lld
    ",ans);
    23     return 0;
    24 }
    View Code

    E Pink Floyd

    感觉挺神的

    首先考虑没有粉色边的情况:

    随便找一个点 x ,先假设 x 是答案。然后再找一个 x 无法到达的点 y ,若 $x ightarrow y$ ,那么标记 y 为可达,否则将 y 设为答案, x 标记为可达。$mathcal{O}(n)$ 次询问后会标记完所有的点,输出此时的答案即可。其实相当于一个不停转移答案节点的过程,感觉非常妙~

    如果有了粉色的话,首先缩点,然后按照拓扑序:

    先随便找一个度为 0 的 scc ,在里面随便找一个点 x 设为答案;然后找另一个还存在的度为 0 的 scc ,在里面随便找一个点 y ,询问 (x,y)。此时若 $x ightarrow y$ ,把 y 删掉(和上面标记可达的意思差不多);否则将答案转移给 y ,把 x 删掉。注意这里如果一个 scc 里的点被删完了,就要把整个 scc 及它的出边删掉,加进来新的入度为 0 的 scc (类似 toposort )。

    胡一下为什么先删度数为 0 的 scc :(很显然吗?)

    原因是由于粉色边的存在,我们需要利用已有的粉色边。考虑一个状态,我们找到了 $u ightarrow v$ ,即 $u ightarrow v$ 无粉色路径有绿色边,但已知 $v ightarrow u$ 有粉色路径。这个时候按照我们的算法会把 v 抛弃,将答案转移到 u 上来,但是事实上 v 有可能成为最终答案,如果擅自转移到 u 可能最终无法得出答案,找不到 v 。但是如果按照拓扑序来更新,每次都取度为拓扑序最靠前的 scc ,这样就不会出现 $u ightarrow v$ 无粉色路径有绿色边, $v ightarrow u$ 有粉色路径的情况,所以每次转移都是对的。

     1 #include<bits/stdc++.h>
     2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
     3 #define Vi vector<int>
     4 
     5 using namespace std;
     6 
     7 const int N=1e5+10;
     8 int n,m,x[N],y[N],cnt,head[N];
     9 int dfn[N],low[N],stk[N],bel[N],top,clk,scc,rd[N]; Vi V[N];
    10 int q[N],Head[N]; set<int> S[N];
    11 struct edge{int to,nxt;}e[N<<1];
    12 
    13 void adde(int *head,int x,int y){
    14     e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt;
    15 }
    16 
    17 void tarjan(int u){
    18     dfn[u]=low[u]=++clk; stk[++top]=u;
    19     for (int i=head[u],v;i;i=e[i].nxt)
    20         if (v=e[i].to,!dfn[v]){
    21             tarjan(v);
    22             low[u]=min(low[u],low[v]);
    23         } else if (!bel[v]) low[u]=min(low[u],dfn[v]);
    24     if (dfn[u]!=low[u]) return;
    25     ++scc;
    26     while (stk[top]!=u) V[scc].push_back(stk[top]),bel[stk[top--]]=scc;
    27     V[scc].push_back(stk[top]),bel[stk[top--]]=scc;
    28 }
    29 
    30 int ask(int x,int y){
    31     printf("? %d %d
    ",x,y),fflush(stdout);
    32     scanf("%d",&x); return x;
    33 }
    34 
    35 int main(){
    36     scanf("%d%d",&n,&m);
    37     rep (i,1,m) scanf("%d%d",&x[i],&y[i]),adde(head,x[i],y[i]);
    38     rep (i,1,n) if (!dfn[i]) tarjan(i);
    39     rep (i,1,m)
    40         if (bel[x[i]]!=bel[y[i]]&&!S[bel[x[i]]].count(bel[y[i]])){
    41             adde(Head,bel[x[i]],bel[y[i]]);
    42             rd[bel[y[i]]]++,S[bel[x[i]]].insert(bel[y[i]]);
    43         }
    44     int tail=0;
    45     rep (i,1,scc) if (!rd[i]) q[++tail]=i;
    46     while (tail>1){
    47         int x=V[q[1]].back(),y=V[q[2]].back();
    48         if (!ask(x,y)) swap(q[1],q[2]);
    49         x=q[2]; V[x].pop_back();
    50         if (V[x].empty()){
    51             swap(q[tail],q[2]),tail--;
    52             for (int i=Head[x],v;i;i=e[i].nxt)
    53                 if (v=e[i].to,!--rd[v]) q[++tail]=v;
    54         }
    55     }
    56     printf("! %d
    ",V[q[1]].back()),fflush(stdout);
    57     return 0;
    58 }
    View Code
  • 相关阅读:
    struts2 标签
    Ubutu命令 笔记积累
    虚拟机VirtualBox和Ubutu
    更新PostgreSQL数据
    向PostgreSQL插入数据
    PostgreSQL表依赖性跟踪
    PostgreSQL外部数据
    PostgreSQL分区介绍
    PostgreSQL继承详解
    PostgreSQL模式介绍
  • 原文地址:https://www.cnblogs.com/bestFy/p/10700376.html
Copyright © 2011-2022 走看看