AtCoder Grand Contest 031

Atcoder

A - Colorful Subsequence

description

求\(s\)中本质不同子序列的个数模\(10^9+7\)。两个子序列不同当且仅当存在一种字符在两者中的出现次数不同。

\(|s|\le10^5\)

solution

\(\prod_{i='a'}^{'z'}(\mbox{字符}i\mbox{出现的次数}+1)-1\)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=0,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N=1e5+5;
const int mod=1e9+7;
int n,t[26],ans=1;char s[N];
int main(){
scanf("%d",&n);scanf("%s",s+1);
for(int i=1;i<=n;++i)++t[s[i]-'a'];
for(int i=0;i<26;++i)ans=1ll*ans*(t[i]+1)%mod;
printf("%d\n",(ans+mod-1)%mod);return 0;
}

B - Reversi

description

有一个长为\(n\)的序列,每个位置有个颜色,可以进行若干次操作,操作为选择两个颜色相同的位置,将它们中间的位置全部变成这个颜色,求所有可能出现的颜色序列的方案数模\(10^9+7\)。

\(n,c_i\le2\times10^5\)

solution

\(f_i=f_{i-1}+[lst_i<i-1]f_{lst_i}\)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=0,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N=2e5+5;
const int mod=1e9+7;
int n,f[N],lst[N];
int main(){
n=gi();f[0]=1;
for(int i=1;i<=n;++i){
int a=gi();
f[i]=f[i-1];
if(lst[a]&&lst[a]<i-1)f[i]=(f[i]+f[lst[a]])%mod;
lst[a]=i;
}
printf("%d\n",f[n]);return 0;
}

C - Differ by 1 Bit

description

构造一个\(0\)到\(2^n-1\)的排列,满足首位是\(A\)末位是\(B\),且相邻两个数在二进制下仅相差\(1\)位。

\(n\le 17\)

solution

只需要构造首位是\(0\)末位是\(A \mbox{xor} B\)的排列再将所有数异或\(A\)就行了。

显然,\(\mbox{popcount}(A\mbox{xor}B)\)为偶数时一定无解,否则根据构造方法一定存在一组解。

考虑将模型转化为在\(n\)维超立方体上的游走,需要遍历这个超立方体\(2^n\)个顶点中的每一个。一种可行的策略是,先遍历\(n\)维超立方体中的某个\(n-1\)维超立方体,再一步走到另一个\(n-1\)维超立方体上并遍历之。

在本题中我们要从\(0\)走到\(A\mbox{xor}B\),可以选出一个二进制位\(p\)满足\(A\mbox{xor}B\)在第\(p\)位上值为\(1\),遍历二进制第\(p\)位为\(0\)的所有点组成的\(n-1\)维超立方体后,再去遍历二进制位第\(p\)位为\(1\)的所有点组成的\(n-1\)维超立方体。两个\(n-1\)维超立方体之间的连接点的选取是任意的,只需要满足起点终点二进制位不同的位数是奇数就行了。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=0,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
#define pc(x) __builtin_popcount(x)
int n,A,B,all;
void dfs(int x,int y,int ban){
if(pc(ban^all)==1){printf("%d %d ",y,x^y);return;}
for(int i=0;i<n;++i)
if((~ban>>i&1)&&(x>>i&1))
for(int j=0;j<n;++j)
if((~ban>>j&1)&&i!=j){
dfs(1<<j,y,ban|1<<i);
dfs(x^(1<<i)^(1<<j),y^(1<<i)^(1<<j),ban|1<<i);
return;
}
}
int main(){
n=gi(),A=gi(),B=gi(),all=(1<<n)-1;
if(pc(A^B)&1)puts("YES"),dfs(A^B,A,0);
else puts("NO");
return 0;
}

D - A Sequence of Permutations

description

定义\(f(p,q)\)为一个排列\(c\)满足\(c_{p_i}=q_i\),其中\(p,q\)均是\(1-n\)排列。已知\(a_1=p,a_2=q,a_n=f(a_{n-2},a_{n-1})(n>2)\),给出\(p,q\),求\(a_k\)。

\(n\le10^5,k\le10^9\)

solution

\([f(p,q)]_{p_i}=q_i\to f(p,q)p=q\to f(p,q)=qp^{-1}\)

然后大力列出\(a_i\)的前若干项。

\[a_1=p\\a_2=q\\a_3=qp^{-1}\\a_4=qp^{-1}q^{-1}\\a_5=qp^{-1}q^{-1}pq^{-1}\\a_6=qp^{-1}q^{-1}ppq^{-1}\\a_7=qp^{-1}q^{-1}pqpq^{-1}\\a_8=qp^{-1}q^{-1}pqp^{-1}qpq^{-1}
\]

然后令\(A=qp^{-1}q^{-1}p\),则可归纳证明出\(a_n=Aa_{n-6}A^{-1}(n>6)\)。

所以就做完啦?

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=0,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
#define vi vector<int>
int n,k;vi a[7];
vi readin(){
vi res(n);
for(int i=0;i<n;++i)res[i]=gi()-1;
return res;
}
void print(vi a){
for(int i=0;i<n;++i)printf("%d ",a[i]+1);
puts("");
}
vi inv(vi a){
vi res(n);
for(int i=0;i<n;++i)res[a[i]]=i;
return res;
}
vi mul(vi a,vi b){
vi res(n);
for(int i=0;i<n;++i)res[i]=a[b[i]];
return res;
}
vi fastpow(vi a,int b){
vi res(n);
for(int i=0;i<n;++i)res[i]=i;
while(b){if(b&1)res=mul(res,a);a=mul(a,a);b>>=1;}
return res;
}
int main(){
n=gi();k=gi();
vi p=readin(),q=readin();
a[1]=p;a[2]=q;
for(int i=3;i<=6;++i)a[i]=mul(a[i-1],inv(a[i-2]));
vi A=mul(mul(q,inv(p)),mul(inv(q),p)),B=fastpow(A,(k-1)/6);
print(mul(mul(B,a[(k-1)%6+1]),inv(B)));return 0;
}

E - Snuke the Phantom Thief

description

二维平面上有\(n\)个珠宝,第\(i\)个珠宝的位置为\((x_i,y_i)\),权值为\(v_i\)。你可以从中选取任意数量的珠宝,但需要满足如下\(m\)条限制:所有(横/纵)坐标(小/大)于等于\(a_i\)的珠宝中至多选\(b_i\)个。求能得到的最大权值和。

\(n \le80,m\le 320,x_i,y_i,a_i\le100,v_i\le10^{15},b_i<n\)

solution

首先这数据范围看着就很费用流

先考虑一维怎么做。

一个很妙的转化是:限制横坐标\(\le a_i\)的珠宝里至多选\(b_i\)个,等价于选择的横坐标第\(b_i+1\)小的珠宝,其横坐标必须\(> a_i\)。

如果是限制横坐标\(\ge a_i\)的珠宝至多选\(b_i\)个,则可以先枚举选\(k\)个珠宝,然后限制就等价于选择的第\(k-b_i\)个珠宝其横坐标必须\(<a_i\)。(以上只考虑\(b_i<k\)的限制,\(b_i\ge k\)的限制显然无效)

这样我们就可以得到\(k\)个二元组\((l_j,r_j)\),分别表示第\(j\)个珠宝的横坐标的范围限制。注意这\(k\)个二元组的\(l\)和\(r\)应满足单调不降。

这样我们就得到了一个匹配的模型:二分图一侧有\(k\)个点,另一侧有\(n\)个点,满足范围限制的点之间连边,然后求一组最大权匹配即可。

至于二维的问题,可以直接把图拆成三份,即左侧\(k\)个点表示横坐标的限制,中间\(2n\)个点内部连权值的边表示珠宝,右侧另\(k\)个点表示纵坐标的限制。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
ll gi(){
ll x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=0,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
int gc(){
char ch=getchar();
while(ch<'A'||ch>'Z')ch=getchar();
return ch;
}
const int N=405;
struct edge{int to,nxt,w;ll cost;}E[N*N];
int n,m,x[N],y[N],t[N],a[N],b[N],L[N],R[N],D[N],U[N],head[N],cnt,S,T,vis[N],pe[N];
ll v[N],dis[N],ans;queue<int>Q;
void link(int u,int v,ll w){
E[++cnt]=(edge){v,head[u],1,w};head[u]=cnt;
E[++cnt]=(edge){u,head[v],0,-w};head[v]=cnt;
}
bool spfa(ll &res){
memset(dis,63,sizeof(dis));
dis[S]=0;Q.push(S);
while(!Q.empty()){
int u=Q.front();Q.pop();vis[u]=0;
for(int i=head[u],v;i;i=E[i].nxt)
if(E[i].w&&dis[v=E[i].to]>dis[u]+E[i].cost){
dis[v]=dis[u]+E[i].cost;pe[v]=i;
if(!vis[v])vis[v]=1,Q.push(v);
}
}
if(dis[T]==dis[0])return false;res+=dis[T];
for(int i=T;i!=S;i=E[pe[i]^1].to)--E[pe[i]].w,++E[pe[i]^1].w;
return true;
}
ll cal(int k){
for(int i=1;i<=k;++i)L[i]=D[i]=0,R[i]=U[i]=233;
for(int i=1;i<=m;++i)
if(b[i]<k){
if(t[i]=='L')L[b[i]+1]=a[i]+1;
if(t[i]=='R')R[k-b[i]]=a[i]-1;
if(t[i]=='D')D[b[i]+1]=a[i]+1;
if(t[i]=='U')U[k-b[i]]=a[i]-1;
}
for(int i=2;i<=k;++i)L[i]=max(L[i],L[i-1]),D[i]=max(D[i],D[i-1]);
for(int i=k-1;i;--i)R[i]=min(R[i],R[i+1]),U[i]=min(U[i],U[i+1]);
memset(head,0,sizeof(head));cnt=1;S=n+k<<1|1;T=n+k+1<<1;
for(int i=1;i<=n;++i)link(i,n+i,-v[i]-1000000000000000ll);
for(int i=1;i<=k;++i){
link(S,n+n+i,0);link(n+n+k+i,T,0);
for(int j=1;j<=n;++j){
if(L[i]<=x[j]&&x[j]<=R[i])link(n+n+i,j,0);
if(D[i]<=y[j]&&y[j]<=U[i])link(n+j,n+n+k+i,0);
}
}
ll res=0;while(spfa(res));return -res-1000000000000000ll*k;
}
int main(){
n=gi();
for(int i=1;i<=n;++i)x[i]=gi(),y[i]=gi(),v[i]=gi();
m=gi();
for(int i=1;i<=m;++i)t[i]=gc(),a[i]=gi(),b[i]=gi();
for(int i=1;i<=n;++i)ans=max(ans,cal(i));
printf("%lld\n",ans);return 0;
}

F - Walk on Graph

description

有一张\(n\)点\(m\)边无向图和一个奇数\(mod\),边有边权,给出\(q\)组询问,每次询问是否存在一条从\(s\)走到\(t\)的长度对\(mod\)取模后等于\(r\)的路径。路径不要求是简单路径,即可以来回走。这里路径长度的定义并非所有边的长度之和,而是经过的第\(i\)条边的长度乘上\(2^i\)之和。

\(n,m,q \le5\times10^4,3\le mod\le 10^6,2\nmid mod\)

solution

把路径反过来,问题转化成了:

一个人初始在\(t\)点,有一个数\(x\)初值为\(0\)。每当他走过一条长度为\(c\)的边,数\(x\)就会变成\(2x+c\)并对\(mod\)取模,问是否存在一条路径使走到\(s\)点后数\(x\)恰好等于\(r\)。

这样暴力的做法就是建立\(n\times mod\)个状态然后连边,询问就是查询状态\((t,0)\)是否可以到达状态\((s,r)\)。

通过观察可以发现一些性质:

1、所有的边(指状态之间的连边)都是双向的。考虑一条边\((a,b,c)\),通过这条边可以实现\((a,x)\to(b,2x+c)\to(a,4x+3c)\to(b,8x+7c)\to...\),而最终一定可以回到状态\((a,x)\)。证明略显然在模意义下每个\(x\)都有唯一对应的\(2x+c\)以及唯一对应的\(\frac{x-c}{2}\),所以走一个长度为偶数的环就能回到原状态了。

2、如果存在两条边\((u,v,a)\)和\((u,w,b)\),那么就有\((u,x)\to(v,2x+a)\to(v,4x+3a)\)以及\((u,x)\to(w,2x+b)\to(u,4x+3b)\),也就是说状态\((u,4x+3a)\)与状态\((u,4x+3b)\)是等价的,即状态\((u,x)\)与状态\((u,x+3(a-b))\)是等价的。

由此,我们可以求出所有有公共点的边的边权之差的\(\gcd\)(由于图是连通的,所以这等价于任意两条边边权之差的\(\gcd\)),设之为\(g\),则对于所有点而言,状态\((u,x)\)与状态\((u,x+3g)\)均是等价的。那么我们就可以令\(mod \gets \gcd(mod,3g)\)了。

观察到此时每条边的边权\(\mod g\)都是相等的,不妨设为\(z\)。可以考虑通过一些奇技淫巧把\(z\)去掉:把所有边权减去\(z\),同时把所有状态的第二维抬高\(z\),即原本的\((u,x)\to(v,2x+c)\)变成\((u,x+z)\to(v,2(x+z-z)+(c-z)+z)=(v,2x+c)\)。这样转化后所有转移都可以与转化前一一对应,因此正确性不存在问题,而经过这样的转化后,我们可以发现每次转移后非\(x\)项加上的常数都是\(g\)的倍数。因此,从一个状态\((u,x)\)出发能够到达的所有状态,一定都可以被表示成\((v,px+qg)\)的形式,其中\(p\)是\(2\)的非负整数次幂,\(q\)是任意非负整数,进一步的,因为\(mod|3g\),故\(q\in\{0,1,2\}\)。

而因为\((u,x)\to(v,2x+c)\to(u,4x+3c)=(u,4x)\),所以\(p\in\{1,2\}\)。于是,我们便可以设计总共\(6n\)个状态表示\((u,px+qg)\),其中\(u\in[1,n],p\in\{1,2\},q\in\{0,1,2\}\)。

这样询问时就需要询问\((t,z)\)能否到达\((s,r+z)\)。相当于我们需要找到一组\((p,q),p\in N^*,q\in\{0,1,2\}\),满足\((t,x)\)与\((s,(p\mod 2)x+qg)\)连通且\(pz+qg=r+z\)。所以可以预处理出\([1,mod)\)中每个数是否等于某个\(2\)的奇数/偶数次幂,然后对于每组询问直接枚举\((p,q)\)即可。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=0,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N=1e6+5;
int n,m,q,mod,a[N],b[N],c[N],g,z,fa[N],chk[2][N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void link(int x,int y){fa[find(x)]=find(y);}
int id(int u,int x,int y){return u*6+x*2+y;}
int main(){
n=gi();m=gi();q=gi();mod=gi();
for(int i=1;i<=m;++i){
a[i]=gi(),b[i]=gi(),c[i]=gi();
g=__gcd(g,abs(c[i]-c[1]));
}
if(!g)g=mod;mod=__gcd(mod,3*g);z=c[1]%g;
for(int i=0;i<n*6;++i)fa[i]=i;
for(int i=1;i<=m;++i){
int u=a[i]-1,v=b[i]-1,w=(c[i]-z)/g%3;
for(int x=0;x<3;++x){
link(id(u,x,0),id(v,(2*x+w)%3,1));
link(id(u,x,1),id(v,(2*x+w)%3,0));
link(id(v,x,0),id(u,(2*x+w)%3,1));
link(id(v,x,1),id(u,(2*x+w)%3,0));
}
}
for(int i=0,j=z;i<mod<<1;++i,j=(j<<1)%mod)chk[i&1][j]=1;
while(q--){
int s=gi()-1,t=gi()-1,r=gi(),res=0;
for(int x=0;x<3;++x)
for(int y=0;y<2;++y)
if(find(id(t,0,0))==find(id(s,x,y)))
res|=chk[y][(r+z+(3-x)*g)%mod];
puts(res?"YES":"NO");
}
return 0;
}

AtCoder Grand Contest 031 简要题解的更多相关文章

  1. AtCoder Grand Contest 039 简要题解

    从这里开始 比赛目录 Problem A Connection and Disconnection 简单讨论即可. Code #include <bits/stdc++.h> using ...

  2. AtCoder Grand Contest 040 简要题解

    从这里开始 比赛目录 A < B < E < D < C = F,心情简单.jpg. Problem A >< 把峰谷都设成 0. Code #include &l ...

  3. AtCoder Grand Contest 035 简要题解

    从这里开始 题目目录 Problem A XOR Circle 你发现,权值的循环节为 $a_0, a_1, a_0\oplus a_1$,然后暴力即可. Code #include <bits ...

  4. AtCoder Grand Contest 036 简要题解

    从这里开始 比赛目录 Problem A Triangle 考虑把三角形移到和坐标轴相交,即 然后能够用坐标比较简单地计算面积,简单构造一下就行了. Code #include <bits/st ...

  5. AtCoder Grand Contest 037 简要题解

    从这里开始 题目目录 Problem A Dividing a String 猜想每段长度不超过2.然后dp即可. 考虑最后一个长度大于等于3的一段,如果划成$1 + 2$会和后面相同,那么划成$2 ...

  6. AtCoder Grand Contest 038 简要题解

    从这里开始 比赛目录 Problem A 01 Matrix Code #include <bits/stdc++.h> using namespace std; typedef bool ...

  7. AtCoder Grand Contest 031题解

    题面 传送门 题解 比赛的之后做完\(AB\)就开始发呆了--简直菜的一笔啊-- \(A - Colorful\ Subsequence\) 如果第\(i\)个字母选,那么它前面任意一个别的字母的选择 ...

  8. AtCoder Grand Contest 031 B - Reversi

    https://atcoder.jp/contests/agc031/tasks/agc031_b B - Reversi Time Limit: 2 sec / Memory Limit: 1024 ...

  9. AtCoder Grand Contest 031 B - Reversi(DP)

    B - Reversi 题目链接:https://atcoder.jp/contests/agc031/tasks/agc031_b 题意: 给出n个数,然后现在你可以对一段区间修改成相同的值,前提是 ...

随机推荐

  1. Unity中DOTween插件的DOTweenPath轨迹移动

    先来看一下DOTweenPath组件的截图 1.Scene View Commands (1)SHIFT+CTRL:add a waypoint        加一个轨迹点 (2)SHIFT+ALT: ...

  2. CF712E [Memort and Casinos]

    题意 每次询问一段区间[l,r],求从最左边走到最右边(r+1)的概率(若走到l-1,则GG了),每个点上写有向右走的概率.支持单点修改. 思考 若只查询一次,那只要知道每个点在不走到l-1的情况下, ...

  3. python中的各种模块(np,os,shutill)

    PS:本博文摘抄自中国慕课大学上的课程<Python数据分析与展示>,推荐刚入门的同学去学习,这是非常好的入门视频. #np模块 .ndim :维度 .shape :各维度的尺度 (2,5 ...

  4. jdk8-全新时间和日期api

    1.jdk8日期和时间api是线程安全的 1.java.time  处理日期时间 2.java.time.temporal: 时间校正器.获取每个月第一天,周几等等 3.java.time.forma ...

  5. Tensorflow函数:tf.zeros

    tf.zeros函数 tf.zeros( shape, dtype=tf.float32, name=None ) 定义在:tensorflow/python/ops/array_ops.py. 创建 ...

  6. python 时间格式化

    2018-10-25 17:49:51 监控软件 原始 2018-10-2517:49:51 切割后 2018/10/25 17:49:51 格式化后 shijian = ' '.join(respo ...

  7. php 安装过程 第二次探索

    由于第一次安装过程写的比较乱,再做整理 1.phpstudy 才是正确姿势. http://phpstudy.php.cn/ 官网下载.  phpstrom 中配置 php为 phpstudy目录下 ...

  8. HDU 6124 17多校7 Euler theorem(简单思维题)

    Problem Description HazelFan is given two positive integers a,b, and he wants to calculate amodb. Bu ...

  9. web(四)html表单类标签

    表单类标签 操作者用于输入信息,并将信息提交给服务器的标签集合. 表单标签介绍 form标签:表单元素(其余标签)标签的容器标签 input标签:用于用户信息输入的标签. button标签:按钮标签. ...

  10. 第七十五课 图的遍历(DFS)

    添加DFS函数: #ifndef GRAPH_H #define GRAPH_H #include "Object.h" #include "SharedPointer. ...