AtCoder Grand Contest 012

A - AtCoder Group Contest

翻译

有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大值定义为三个人中第二强的人的强大值。求\(n\)组最大的强大值之和。

题解

这。。。不是倒着选两个人,正着选一个人构成一组就好了嘛。。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[300100];ll ans;
int main()
{
n=read();
for(int i=1;i<=n*3;++i)a[i]=read();
sort(&a[1],&a[n+n+n+1]);
for(int i=n+n+n-1,j=1;j<=n;++j,i-=2)ans+=a[i];
cout<<ans<<endl;
return 0;
}

B - Splatter Painting

翻译

给你一张无向图,每次给定一个点\(u_i\),让你把距离这个点不超过\(d_i\)的所有点都染成颜色\(c_i\)。输出最终每个点的颜色。

题解

这种染色问题倒着做。对于每个点记录一个它染出去的最远距离,因为\(d\)很小,所以这个值最多被改变\(d\)次。那么暴力即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define MAX 100100
#define ll long long
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,m,col[MAX],dis[MAX];
int Q,U[MAX],D[MAX],C[MAX];
void BFS(int S,int d,int c)
{
if(dis[S]>=d)return;dis[S]=d;
queue<int> Q;Q.push(S);
while(!Q.empty())
{
int u=Q.front();Q.pop();
if(!col[u])col[u]=c;
if(!dis[u])continue;
for(int i=h[u];i;i=e[i].next)
if(dis[e[i].v]<dis[u]-1)
{
dis[e[i].v]=dis[u]-1;
Q.push(e[i].v);
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
Q=read();memset(dis,-1,sizeof(dis));
for(int i=1;i<=Q;++i)U[i]=read(),D[i]=read(),C[i]=read();
for(int i=Q;i;--i)
BFS(U[i],D[i],C[i]);
for(int i=1;i<=n;++i)printf("%d\n",col[i]);
return 0;
}

C - Tautonym Puzzle

翻译

构造一个长度不超过\(200\),字符集为\([1,100]\)的串。使得其中能够从中间分成左右两个完全一样的子序列的个数恰好为\(n\)。

题解

一个很\(naive\)的想法就是把\(n\)二进制分解,然后对于每一个二的若干次幂找一段连续的构建一下,长成\(123...123...\)这个样子。然而发现这样子的数字以及长度似乎都爆炸了。

换个方式,我们来倍增,假设\(XY\)这个串有\(ans\)对,那么\(aXaY\)就有\(2ans\)对,\(aXYa\)就有\(ans+1\)对。那么就很资磁了,倍增一下就好了。

至于为什么代码里\(n\)要加一,因为如果只按代码的方式做的话你会发现少了一对。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
#define ll long long
ll n;int cnt;
deque<int> Q1,Q2;
void Work(ll n)
{
if(n==1)return;
Work(n>>1);++cnt;
Q1.push_front(cnt);Q2.push_front(cnt);
if(n&1)++cnt,Q1.push_front(cnt),Q2.push_back(cnt);
}
int main()
{
cin>>n;Work(n+1);
printf("%d\n",(int)(Q1.size()+Q2.size()));
while(!Q1.empty())printf("%d ",Q1.front()),Q1.pop_front();
while(!Q2.empty())printf("%d ",Q2.front()),Q2.pop_front();
puts("");return 0;
}

D - Colorful Balls

翻译

有\(n\)个球排成一行,每一个球都有颜色和重量,如果两个球颜色相同并且重量和小于\(X\)那么他们可以交换位置,如果两个球颜色不同并且重量和小于\(Y\)那么他们可以交换位置。求有多少种不同的颜色序列。

题解

不难发现如果\(u\)可以和\(a,b\)交换,那么\(u,a,b\)就可以任意交换位置。那么如果我们把可以交换位置的球用边给连起来,那么一个联通块中的球可以任意交换位置。因此,一个联通块的贡献就是球的个数的阶乘除掉每种颜色球的个数的阶乘。而最终的答案显然就是所有联通块的乘积。

先考虑同色的连边。显然如果一个球能够和同色的连边,那么它必定可以和同色的重量最小的球连边,而连边方式有两种,第一种是和同色最小值的和小于\(X\),这样子可以直接连边。第二种连边方式则是这个球和异色最小值的和小于\(Y\),那么同色的最小值也必定可以和这个异色最小值连边,也就是这个球可以和同色最小值连边。如果我们把同色球按照重量排序,显然一个前缀之间都可以互换。而不能互换的显然是一段后缀,并且显然它们也不能和任何异色球连边,因为它们不能和异色最小值连边,也必定不能和其他异色的球连边。那么我们处理完所有同色的球之后,显然同色球可以合在一起考虑,只需要考虑重量最小的球对外的连边。那么再按照重量排一遍序,显然可以链接的一定是一段前缀,那么直接考虑这一段前缀的贡献就好了。

我竟然做出来了?这题真简单?

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define MAX 200200
#define MOD 1000000007
using namespace std;
#define ll long long
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,X,Y,ans;
struct Ball{int c,w;}B[MAX];
bool operator<(Ball a,Ball b){if(a.c!=b.c)return a.c<b.c;return a.w<b.w;}
int jc[MAX],jv[MAX],inv[MAX];
int pmn[MAX],smn[MAX],sz[MAX],W[MAX],p[MAX];
bool cmp(int i,int j){return W[i]<W[j];}
int main()
{
n=read();X=read();Y=read();
for(int i=1;i<=n;++i)B[i].c=read(),B[i].w=read();
jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD,jv[i]=1ll*jv[i-1]*inv[i]%MOD;
sort(&B[1],&B[n+1]);
memset(pmn,63,sizeof(pmn));
memset(smn,63,sizeof(smn));
memset(W,63,sizeof(W));
for(int i=1,j;i<=n;i=j+1)
{
j=i;while(j<n&&B[i].c==B[j+1].c)++j;
pmn[B[i].c]=smn[B[i].c]=B[i].w;
}
for(int i=1;i<=n;++i)pmn[i]=min(pmn[i],pmn[i-1]);
for(int i=n;i>=1;--i)smn[i]=min(smn[i],smn[i+1]);
for(int i=1,j;i<=n;i=j+1)
{
j=i;while(j<n&&B[i].c==B[j+1].c)++j;
int d=min(pmn[B[i].c-1],smn[B[i].c+1]);
int cnt=1;
for(int k=i+1;k<=j;++k)
if(B[i].w+B[k].w<=X||B[k].w+d<=Y)++cnt;
else break;
sz[B[i].c]=cnt;W[B[i].c]=B[i].w;
}
for(int i=1;i<=n;++i)p[i]=i;
sort(&p[1],&p[n+1],cmp);
for(int i=1;i<=n;++i)
if(i==1||W[p[i]]+W[p[1]]<=Y)
ans+=sz[p[i]];
ans=jc[ans];
for(int i=1;i<=n;++i)
if(i==1||W[p[i]]+W[p[1]]<=Y)
ans=1ll*ans*jv[sz[p[i]]]%MOD;
printf("%d\n",ans);
return 0;
}

E - Camel and Oases

翻译

有\(n\)个绿洲排成一条直线,有一只骆驼,可以带体积为\(V\)的水,在绿洲可以补满水。现在骆驼要访问所有的绿洲,它有两种运动方式,第一种直接走过去,要求两个绿洲直接的距离不能超过\(V\)。另外一种是跳过去,\(V\)变成\([\frac{V}{2}]\),然后直接到达,当\(V=0\)时就不能跳了。

现在对于每一个绿洲作为起点,回答能否遍历所有的绿洲。

题解

发现\(V/2^k\)一共就\(log\)种取值,显然可以预处理出对于每一个起点,\(V=V/2^k\)时能够到达的区间。这样子我们就得到了一条条线段。

那么问题等价于回答,对于每一个\(V/2^k\),从中分别选出一条线段,然后每次钦定\(k=0\)的线段,问能否覆盖整个区间。

然后就状压了!设\(sl[S]\)表示用了\(S\)集合中的这些长度的线段(\(V/2^k\)),能够覆盖\([1,x]\)的最大\(x\)值,同理预处理\(sr[S]\),这样子我们就可以知道左右侧能够拼出来的位置。那么每个询问只需要考虑中间一段加入进来之后能够把整个线段联通即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
void cmax(int &x,int y){if(x<y)x=y;}
void cmin(int &x,int y){if(x>y)x=y;}
int n,V[25],x[MAX],t;
int L[20][MAX],R[20][MAX],mr[MAX];
int sl[1<<19],sr[1<<19];
int main()
{
n=read();V[0]=read();
for(int i=1;i<=n;++i)x[i]=read();
while(V[t])V[t+1]=V[t]>>1,++t;int S=1<<t;
for(int i=0;i<=t;++i)
{
L[i][1]=1;R[i][n]=n;
for(int j=2;j<=n;++j)
if(x[j]-x[j-1]<=V[i])L[i][j]=L[i][j-1];
else L[i][j]=j;
for(int j=n-1;~j;--j)
if(x[j+1]-x[j]<=V[i])R[i][j]=R[i][j+1];
else R[i][j]=j;
}
for(int i=1;i<S;++i)sl[i]=1,sr[i]=n;
sl[0]=0;sr[0]=n+1;
for(int i=0;i<S;++i)
for(int j=1;j<=t;++j)
if(!(i&(1<<(j-1))))
{
cmax(sl[i|(1<<(j-1))],R[j][sl[i]+1]);
cmin(sr[i|(1<<(j-1))],L[j][sr[i]-1]);
}
memset(mr,63,sizeof(mr));
for(int j=0;j<S;++j)cmin(mr[sl[j]],sr[(S-1)^j]);
for(int i=n;~i;--i)mr[i]=min(mr[i],mr[i+1]);
for(int i=1;i<=n;++i)puts(mr[L[0][i]-1]<=R[0][i]+1?"Possible":"Impossible");
return 0;
}

F - Prefix Median

翻译

给定一个长度为\(2n-1\)的数列\(a_i\),定义数列\(b_i\)为\((a_1,a_2,...,a_{2i-1})\)的中位数。

现在对于\(a\)的任意一种排列,有多少种本质不同的\(b\)。

对于\(10^9+7\)取模。

\(n\le 50\)

题解

一个\(2^{2n-1}\)的想法是状压已经用过了哪些\(a_i\),每次选出两个进行更新就可以了。

考虑每次加入两个数,中位数会怎么变化。讨论一下,要么加入一个比中位数大的和一个比中位数小的,然后中位数不变,要么加入两个比中位数小或者大的,改变成前驱或者后继。

因此,我们得到两个结论:1.\(b_i\)的取值范围一定在排序之后的\(a[i]\)到\(a[2n-i]\)之间。2.不存在\(i,j,i<j\),满足\(b_j<b_i<b_{j+1}\)或者\(b_j>b_i>b_{j+1}\)。

我们唯一知道的状态就是\(b_n\)一定是唯一确定的,那么我们考虑反过来做,设\(f[i][l][r]\)表示当前考虑到\(b_i\),还有\(l\)个数\(\le b_{i+1}\),有\(r\)个数\(\gt b_{i+1}\),并且这些数都可以作为当前\(b_i\)的可能取值的方案数。

那么我们的初值就是\(f[n][1][0]=1\),确定一下两侧范围自然扩大,能否把左右选择的范围变大,增量记为\(pl,pr\)。

每次转移的时候枚举一下选择范围内的哪一个数作为接下来的\(b_i\),更新一下\(l,r\)就好了。

这里有一个问题,就是我们是否能否在前面已经确定\(b_{i+1..n}\)的情况下,可以任意选择\(b_i\),答案是能。 我们这样子思考,如果接下来我们要往左挪动,那么我们删去右侧最小的两个数,那么就可以取到接下来可选数中的最大值;如果接下来我们向右挪动,那么我们删去左侧最大的两个数,这样子就可以取到接下来的最小值;如果我们当前位置不变,左侧删去最右的一行、右侧删去最左的一行就可以取到接下来可能的最大最小值。因此我们接下来可以选择任意在\(l,r\)限制内的\(b\)。

那么我们现在只需要计算一下方案数就行了。

考虑\(l,r\)怎么更新,假设我们向左选择了第\(L\)个数,那么显然这之间的都要删去,否则就无法直接跳到这一行了(注意这个操作是变为前驱,即中间的所有值都在某一步中被删去了),而右侧的范围自然拓展,再加上原本的位置变为合法位置,所以转移是\(f[i+1][l][r]\rightarrow f[i][l-L+pl][r+pr+(L>1)]\)。

如果我们向右选择了第\(R\)个数,那么类似的得到转移\(f[i+1][l][r]\rightarrow f[i][l+pl+1][r+pr-R]\)。

两个转移的微小区别在于状态中\(l\)是小于等于,而\(r\)是大于。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 1000000007
#define MAX 101
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX],f[MAX][MAX][MAX],ans;
int main()
{
n=read();for(int i=1;i<n+n;++i)a[i]=read();
sort(&a[1],&a[n+n]);
f[n][1][0]=1;
for(int i=n-1;i;--i)
{
int pl=(a[i]!=a[i+1]),pr=(a[n+n-i]!=a[n+n-i-1]);
for(int l=0;l<=n+n-1;++l)
for(int r=0;l+r<=n+n-1;++r)
if(f[i+1][l][r])
{
for(int L=1;L<=l+pl;++L)add(f[i][l+pl-L+1][r+pr+(L>1)],f[i+1][l][r]);
for(int R=1;R<=r+pr;++R)add(f[i][l+pl+1][r+pr-R],f[i+1][l][r]);
}
}
for(int i=0;i<n+n;++i)
for(int j=0;j<n+n;++j)
add(ans,f[1][i][j]);
printf("%d\n",ans);
return 0;
}

AtCoder Grand Contest 012的更多相关文章

  1. AtCoder Grand Contest 012 A

    A - AtCoder Group Contest Time limit : 2sec / Memory limit : 256MB Score : 300 points Problem Statem ...

  2. AtCoder Grand Contest 012 A - AtCoder Group Contest(贪心)

    Time limit : 2sec / Memory limit : 256MB Score : 300 points Problem Statement There are 3N participa ...

  3. AtCoder Grand Contest 012 C:Tautonym Puzzle

    题目传送门:https://agc012.contest.atcoder.jp/tasks/agc012_c 题目翻译 如果一个字符串是好的,那么这个字符串的前半部分和后半部分肯定一模一样.比如\(a ...

  4. AtCoder Grand Contest 012 D:Colorful Balls

    题目传送门:https://agc012.contest.atcoder.jp/tasks/agc012_d 题目翻译 给你一排一共\(N\)个球,每个球有一个颜色\(c_i\)和一个重量\(w_i\ ...

  5. AtCoder Grand Contest 012 B - Splatter Painting(dp)

    Time limit : 2sec / Memory limit : 256MB Score : 700 points Problem Statement Squid loves painting v ...

  6. AtCoder Grand Contest 012 B Splatter Painting (反向处理 + 记忆化)

    题目链接  agc012 Problem B 题意  给定一个$n$个点$m$条边的无向图,现在有$q$个操作.对距离$v$不超过$d$的所有点染色,颜色编号为$c$. 求每个点最后的颜色状态. 倒过 ...

  7. AtCoder Grand Contest 012 B

    B - Splatter Painting Time limit : 2sec / Memory limit : 256MB Score : 700 points Problem Statement ...

  8. AtCoder Grand Contest 012 D Colorful Balls

    题意: 有N个球排成一行,第i个球颜色为ci, 权为wi, 如果两个同色球权值和 <= X 则它们可以交换: 如果两个异色球权值和 <= Y 则它们可以交换:不限制交换次数,求能到达的颜色 ...

  9. AtCoder Grand Contest 012 B Splatter Painting(记忆化搜索)

    题意: 给一个包含N个顶点,M条边,无自环和重边的简单无向图,初始每个点颜色都为0,每条边的长度为1,连接着ai,bi两个节点.经过若干个操作, 每次将与某个点vi距离不超过di的所有点染成某种颜色c ...

随机推荐

  1. 使用ip开头的工具,而不是只会ifconfig

    结论: 1.使用ip 开头的工具,比ifconfig显示的信息更多,并且支持的功能更强大. 2.常用的功能有: 显示接口基本信息: ip link show dev eth0 设置端口up/down: ...

  2. Bitmap上下合成图片

    合成两张图片,上下叠加的效果: /** * 把两个位图覆盖合成为一个位图,以底层位图的长宽为基准 * * @param backBitmap 在底部的位图 * @param frontBitmap 盖 ...

  3. Vysor破解助手for Linux and macOS

    <Vysor Pro破解助手>提供了Windows下的Vysor破解工具,为了使用Linux及macOS同学的方便,最近整理了Linux及macOS版的Vysor破解助手. Linux版V ...

  4. 我认知的javascript之作用域和闭包

    说到javascript,就不得不说javascript的作用域和闭包:当然,还是那句老话,javascript在网上都说得很透彻了,我也就不过多的强调了: 作用域:javascript并没有像其他的 ...

  5. python基础篇实战

    1. 判断下面的结果 # 1. 判断下面的结果 # 1 > 1 or 3 < 4 or 4 > 5 and 2 > 1 and 9 > 8 or 7 < 6 pri ...

  6. 英语口语练习系列-C01-好棒

    古诗 来自百度 It's cool. It is neat. It's righteous! It's righteous! 酷毙了! righteous是 cool 的高级了 如果一件事让你无法用 ...

  7. SQL NOT NULL 约束

    SQL NOT NULL 约束 NOT NULL 约束强制列不接受 NULL 值. NOT NULL 约束强制字段始终包含值.这意味着,如果不向字段添加值,就无法插入新记录或者更新记录. 下面的 SQ ...

  8. SpringBoot四大神器之Actuator

    介绍 Spring Boot有四大神器,分别是auto-configuration.starters.cli.actuator,本文主要讲actuator.actuator是spring boot提供 ...

  9. yafu安装使用方法以及mismatched parens解决方法

    yafu用于自动整数因式分解,在RSA中,当p.q的取值差异过大或过于相近的时候,使用yafu可以快速的把n值分解出p.q值,原理是使用Fermat方法与Pollard rho方法等. 再摘录官方的一 ...

  10. HNOI2019:My Dream

    反正这次的目标也不是进省队,目标就是做到最好吧-- 下面都是流水账~ Day -INF ~ Day -3 专题交流没什么好说的,模拟赛详见3.11-3.27省选前多校联考乱记和3.28-4.2CJ大毒 ...