AtCoder Grand Contest 031 简要题解
AtCoder Grand Contest 031
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=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 简要题解的更多相关文章
- AtCoder Grand Contest 039 简要题解
从这里开始 比赛目录 Problem A Connection and Disconnection 简单讨论即可. Code #include <bits/stdc++.h> using ...
- AtCoder Grand Contest 040 简要题解
从这里开始 比赛目录 A < B < E < D < C = F,心情简单.jpg. Problem A >< 把峰谷都设成 0. Code #include &l ...
- AtCoder Grand Contest 035 简要题解
从这里开始 题目目录 Problem A XOR Circle 你发现,权值的循环节为 $a_0, a_1, a_0\oplus a_1$,然后暴力即可. Code #include <bits ...
- AtCoder Grand Contest 036 简要题解
从这里开始 比赛目录 Problem A Triangle 考虑把三角形移到和坐标轴相交,即 然后能够用坐标比较简单地计算面积,简单构造一下就行了. Code #include <bits/st ...
- AtCoder Grand Contest 037 简要题解
从这里开始 题目目录 Problem A Dividing a String 猜想每段长度不超过2.然后dp即可. 考虑最后一个长度大于等于3的一段,如果划成$1 + 2$会和后面相同,那么划成$2 ...
- AtCoder Grand Contest 038 简要题解
从这里开始 比赛目录 Problem A 01 Matrix Code #include <bits/stdc++.h> using namespace std; typedef bool ...
- AtCoder Grand Contest 031题解
题面 传送门 题解 比赛的之后做完\(AB\)就开始发呆了--简直菜的一笔啊-- \(A - Colorful\ Subsequence\) 如果第\(i\)个字母选,那么它前面任意一个别的字母的选择 ...
- AtCoder Grand Contest 031 B - Reversi
https://atcoder.jp/contests/agc031/tasks/agc031_b B - Reversi Time Limit: 2 sec / Memory Limit: 1024 ...
- AtCoder Grand Contest 031 B - Reversi(DP)
B - Reversi 题目链接:https://atcoder.jp/contests/agc031/tasks/agc031_b 题意: 给出n个数,然后现在你可以对一段区间修改成相同的值,前提是 ...
随机推荐
- jdk重装后com.sun.tools.javac.Main is not on the classpath的问题 .
在重装了JDk之后,在编译工程的时候出现如下错误: com.sun.tools.javac.Main is not on the classpath.Perhaps JAVA_HOME does no ...
- jenkins+findbugs+checkstyle+PMD静态代码检查(二)
可以根据自己的需求选中对应的插件进行配置(不一定非要同时配置三个插件) jenkins:持续集成的工具 fundbugs:检测代码静态错误的插件 例如:定义了没有用到的对象,string类型的比较使 ...
- mysql 数据迁移
最近线上系统新挂了一次磁盘,需要将系统磁盘下的 mysql 数据目录迁移到 数据盘上. 经过一番考察,mysql在安装时,使用了预编译的二进制tar.gz包.共有两处配置了 datadir属性 /et ...
- HanLP自然语言处理包介绍
支持中文分词(N-最短路分词.CRF分词.索引分词.用户自定义词典.词性标注),命名实体识别(中国人名.音译人名.日本人名.地名.实体机构名识别),关键词提取,自动摘要,短语提取,拼音转换,简繁转换, ...
- vector容器的注意事项
1.容器是指对象的集合,每一个元素都是一个对象,并且对象的类型相同.可以使用索引去访问容器中的对象. 2.由于容器中存放的是对象,所以引用无法成为vector的成员. 3.容器的初始化,与string ...
- springmvc添加定时任务
springmvc.xml文件中添加如下配置 <bean id="ClearTempRoomLogTask" class="com.test.listener.St ...
- python字符串转换成数字
Action(){ int i; char *s="{str}"; i=atoi(lr_eval_string(s)); lr_output_message("%d&qu ...
- python中处理.db文件借助navicat
navicat premium 12 中可以点击“连接” --sqllite 然后选择.db等文件导入 即可再导成..csv等格式.
- gmtdefaults locate
http://seisman.blog.ustc.edu.cn/index.php/archives/553
- PropertiesUtil 读取properties
package com.midea.clean.util; import java.io.InputStream; import java.io.UnsupportedEncodingExceptio ...