AtCoder Beginner Contest 285 解题报告

\(\text{DaiRuiChen007}\)

Contest Link

A. Edge Checker 2

假设 \(a\ge b\),当且仅当 \(\left\lfloor\dfrac a2\right\rfloor=b\) 时成立

时间复杂度 \(\Theta(1)\)

#include<bits/stdc++.h>
using namespace std;
signed main() {
int a,b;
scanf("%d%d",&a,&b);
if(a<b) swap(a,b);
puts(a/2==b?"Yes":"No");
return 0;
}

B. Longest Uncommon Prefix

对于每个 \(i\) 从小到大不断增加 \(l\) 的值并判断即可

时间复杂度 \(\Theta(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5001;
char s[MAXN];
signed main() {
int n;
scanf("%d%s",&n,s+1);
for(int i=1;i<n;++i) {
int l=0;
while(i+l+1<=n&&s[l+1]!=s[i+l+1]) ++l;
printf("%d\n",l);
}
return 0;
}

C. abc285_brutmhyhiizp

设 \(n=|S|\),字符串下标从 \(1\) 开始

从最高位开始考虑,假设最高位的字母是 \(\texttt C\),那么最高位填 \(\texttt A,\texttt B\) 或空的字符串一定更小,这样的字符串总数是 \(3\times 26^{n-1}\) 个,而最高位填 \(\texttt C\) 的串继续比较下一位即可

对于第二位、第三位……也类似统计即可

时间复杂度 \(\Theta(n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=20;
char s[MAXN];
signed main() {
int ans=0;
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=n,x=1;i>=1;--i,x*=26) ans+=(s[i]-'A'+1)*x;
printf("%lld\n",ans);
return 0;
}

D. Change Usernames

连接所有的 \(S_i\to T_i\),发现得到的图上每个点的出入度 \(\le 1\),因此这张图上只可能有若干个环和若干条链

注意到环一定不行,而链一定可行,因此对原图做拓扑排序判环即可

时间复杂度 \(\Theta(n\log n)\),瓶颈在用 map 实现字符串哈希上

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+1;
map <string,int> rec;
vector <int> G[MAXN];
int siz=0,deg[MAXN];
inline int id(string S) {
if(rec.find(S)==rec.end()) rec[S]=++siz;
return rec[S];
}
signed main() {
int n;
cin>>n;
for(int i=1;i<=n;++i) {
string u,v;
cin>>u>>v;
G[id(u)].push_back(id(v));
++deg[id(v)];
}
queue <int> q;
for(int i=1;i<=siz;++i) if(!deg[i]) q.push(i);
while(!q.empty()) {
int p=q.front(); q.pop();
for(int v:G[p]) {
--deg[v];
if(!deg[v]) q.push(v);
}
}
for(int i=1;i<=siz;++i) {
if(deg[i]>0) {
puts("No");
return 0;
}
}
puts("Yes");
return 0;
}

E. Work or Rest

考虑 dp,用 \(dp_i\) 表示前 \(i\) 天在第 \(i\) 天休息时的最大价值,状态转移方程如下:

\[dp_i=\max_{j=1}^i\{dp_j+\operatorname{cost}(j,i)\}
\]

其中 \(\operatorname{cost}(l,r)\) 表示在 \(l,r\) 两天休息,中间不休息的情况下 \(l+1\sim r-1\) 获得的最大价值,通过前缀和优化可以在 \(\Theta(1)\) 的时间内计算

注意到第 \(n\) 天可能和下周的第一天结合产生贡献,为了解决这个问题,我们不妨把有休假的日子设为第 \(1\) 天,这样答案就是 \(dp_{n+1}\) 了

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5005;
int a[MAXN],sum[MAXN],dp[MAXN];
inline int cost(int l,int r) {
int k=r-l-1;
return sum[(k+1)/2]+sum[k/2];
}
signed main() {
int n;
scanf("%lld",&n);
for(int i=1;i<=n;++i) {
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
memset(dp,-0x3f,sizeof(dp));
dp[1]=0;
for(int i=2;i<=n+1;++i) {
for(int j=1;j<i;++j) {
dp[i]=max(dp[i],dp[j]+cost(j,i));
}
}
printf("%lld\n",dp[n+1]);
return 0;
}

F. Substring of Sorted String

用 \(26\) 棵树状数组分别统计每个字母在特定区间中出现的次数

每次回答询问时先得到区间中的最小字母 \(lo\) 和最大字母 \(hi\),先判断字母 \(lo+1\sim hi-1\) 中的每个字母是不是都全部在 \([l,r]\) 中,然后简单模拟得到在每个字母对应的区间再判断这个区间是否全是该字母即可

时间复杂度 \(\Theta(|\Sigma|\times n\log n)\),\(\Sigma\) 为字符集

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+1;
int n,q;
struct BitTree {
int tree[MAXN];
inline int lowbit(int x) { return x&-x; }
inline void Modify(int x,int v) {
for(;x<=n;x+=lowbit(x)) tree[x]+=v;
}
inline int q(int x) {
int ret=0;
for(;x;x-=lowbit(x)) ret+=tree[x];
return ret;
}
inline int Query(int l,int r) {
return q(r)-q(l-1);
}
} A[26];
char str[MAXN];
signed main() {
scanf("%d%s%d",&n,str+1,&q);
for(int i=1;i<=n;++i) {
A[str[i]-'a'].Modify(i,1);
}
while(q--) {
int op;
scanf("%d",&op);
if(op==1) {
int x; char c;
scanf("%d %c",&x,&c);
A[str[x]-'a'].Modify(x,-1);
str[x]=c;
A[str[x]-'a'].Modify(x,1);
} else {
bool ok=true;
int l,r;
scanf("%d%d",&l,&r);
int lo=26,hi=0;
for(int i=0;i<26;++i) {
if(A[i].Query(l,r)>0) {
lo=min(lo,i),hi=max(hi,i);
}
}
int x=l;
for(int i=lo;i<=hi;++i) {
int k=A[i].Query(l,r);
if(lo<i&&i<hi) if(k<A[i].Query(1,n)) ok=false;
if(A[i].Query(x,x+k-1)!=k) ok=false;
x+=k;
}
puts(ok?"Yes":"No");
}
}
}

G. Tatami

用 \(1\times 2\) 骨牌覆盖网格图立刻想到黑白染色建立二分图,对于已经被 \(1\times 1\) 覆盖的方格先删除,我们只需要为所有 \(c_{i,j}=2\) 的位置找到匹配即可,剩下的位置用 \(1\times 1\) 填补

正解好像是网络流,这里只提供一种乱搞做法:

对于每个 \(c_{i,j}=2\) 的没有匹配的点,直接在二分图上暴力搜出增广路,如果搜不出来增广路则输出 No

时间复杂度 \(\Theta(h^2w^2)\)

注意到实际上很难卡满时间复杂度,因此注意一下实现的常数(例如用时间戳标记对此复用 vis[] 数组避免过多的 memset 操作)即可通过本题

#include<bits/stdc++.h>
using namespace std;
const int MAXN=301,MAXV=1e5+1;
vector <int> G[MAXV];
int tar[MAXV],vis[MAXV];
inline bool dfs(int x,int t) {
if(vis[x]==t) return false;
vis[x]=t;
for(int p:G[x]) {
if(vis[p]==t) continue;
vis[p]=t;
if(tar[p]==-1||dfs(tar[p],t)) {
tar[p]=x,tar[x]=p;
return true;
}
}
return false;
}
const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
char a[MAXN][MAXN];
int id[MAXN][MAXN];
signed main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%s",a[i]+1);
for(int i=1,cnt=0;i<=n;++i) {
for(int j=1;j<=m;++j) {
id[i][j]=++cnt;
}
}
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j) {
if(a[i][j]=='1'||(i+j)%2==0) continue;
for(int k:{0,1,2,3}) {
int x=i+dx[k],y=j+dy[k];
if(x<1||x>n||y<1||y>m) continue;
if(a[x][y]=='1') continue;
G[id[i][j]].push_back(id[x][y]);
G[id[x][y]].push_back(id[i][j]);
}
}
}
memset(tar,-1,sizeof(tar));
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j){
if(a[i][j]=='2') {
if(tar[id[i][j]]!=-1) continue;
if(!dfs(id[i][j],id[i][j])) {
puts("No");
return 0;
}
}
}
}
puts("Yes");
return 0;
}

Ex. Avoid Square Number

显然立刻想到容斥,记 \(S_i\) 为至少存在 \(i\) 个平方数的方案数,那么得到:

\[\text{Answer}=\sum_{i=0}^n (-1)^i\times \binom ni\times S_i
\]

那么原问题转化为求 \(S_i\),\(S_i\) 可以转化为对于每个质因数 \(p_j\),求出把 \(E_j\) 拆成至少 \(i\) 个偶数的方案数的总乘积

考虑一次性处理出对于所有 \(E_j\) 的答案,对使用质因子数量建立生成函数,即 \(F_i(x)=\sum_{k=0}^\infty x^k\times f_{i,k}\),其中 \(f_{i,k}\) 为把 \(k\) 拆成至少 \(i\) 个偶数的方案数,那么我们知道 \(S_i=\prod_{j=1}^n f_{i,E_j}\)

而 \(F_i(x)\) 的值也是一个非常经典的问题,推导形过程如下:

\[\begin{aligned}
F_i(x)
&=(x^0+x^2+x^4+x^6+\cdots)^i\times(x^0+x^1+x^2+x^3+\cdots)^{n-i}\\[2ex]
&=\left(\dfrac{1}{1-x^2}\right)^i\times\left(\dfrac 1{1-x}\right)^{n-i}\\
&=\dfrac{1}{(1-x)^n\times (1+x)^i}
\end{aligned}
\]

注意到我们可以每次暴力卷积计算出 \(F_0(x)\),而每次转移 \(F_{i}(x)\to F_{i+1}(x)\) 只需要乘上 \((1+x)^{-1}=1-x+x^2-x^3+x^4\cdots\) 就可以得到下一个 \(F\)

设 \(w=\max_{i}^k\{E_i\}\),那么所有的多项式都只需要在 \(\bmod x^{w+1}\) 意义下进行

注意到乘 \(\dfrac 1{1-x}\) 等价于做前缀和,乘 \(\dfrac 1{1+x}\) 等价于做前缀差(不等于差分,可以自己推导一下),因此多项式操作的复杂度都是 \(\Theta(w)\)

求出 \(F_0(x)\) 的复杂度是 \(\Theta(nw)\),而接下来依次计算 \(F_1(x)\sim F_n(x)\) 的总复杂度也是 \(\Theta(nw)\),每次通过 \(F_i(x)\) 求 \(S_i\) 的复杂度是 \(\Theta(k)\),执行 \(n\) 次,而容斥的复杂度是 \(\Theta(n)\)

综上,时间复杂度为 \(\Theta(nw+nk)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e4+1,MOD=1e9+7;
int n,k,E[MAXN],p[MAXN];
inline void sum(vector <int> &F) {
for(int i=1;i<MAXN;++i) F[i]=(F[i]+F[i-1])%MOD;
}
inline void del(vector <int> &F) {
for(int i=1;i<MAXN;++i) F[i]=(F[i]+MOD-F[i-1])%MOD;
}
int fac[MAXN],inv[MAXN];
inline int binom(int n,int m) {
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
inline int ksm(int a,int b,int m=MOD) {
int ret=1;
while(b) {
if(b&1) ret=ret*a%m;
a=a*a%m;
b=b>>1;
}
return ret;
}
signed main() {
scanf("%lld%lld",&n,&k);
fac[0]=inv[0]=1;
for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%MOD,inv[i]=ksm(fac[i],MOD-2);
for(int i=1;i<=k;++i) scanf("%lld",&E[i]);
vector <int> F(MAXN);
F[0]=1;
for(int i=1;i<=n;++i) sum(F);
for(int i=0;i<=n;++i) {
p[i]=1;
for(int j=1;j<=k;++j) p[i]=(p[i]*F[E[j]])%MOD;
del(F);
}
int ans=0;
for(int i=0,f=1;i<=n;++i,f*=-1) {
ans=(ans+MOD+f*binom(n,i)*p[i]%MOD)%MOD;
}
printf("%lld\n",ans);
return 0;
}

AtCoder Beginner Contest 285 解题报告的更多相关文章

  1. AtCoder Beginner Contest 122 解题报告

    手速选手成功混进rated only里面的前30名,但是总排名就到110+了... A - Double Helix #include <bits/stdc++.h> #define ll ...

  2. AtCoder Beginner Contest 146解题报告

    题目地址 https://atcoder.jp/contests/abc146/tasks 感觉没有什么有意思的题... 题解 A #include <bits/stdc++.h> usi ...

  3. Atcoder Beginner Contest 124 解题报告

    心态爆炸.本来能全做出来的.但是由于双开了Comet oj一个比赛,写了ABC就去搞那个的B题 还被搞死了. 回来写了一会D就过了.可惜比赛已经结束了.真的是作死. A - Buttons #incl ...

  4. AtCoder Beginner Contest 118 解题报告

    A - B +/- A #include <bits/stdc++.h> int main() { int a, b; std::cin >> a >> b; b ...

  5. AtCoder Beginner Contest 120 解题报告

    为啥最近都没有arc啊... A - Favorite Sound #include <algorithm> #include <iostream> #include < ...

  6. AtCoder Beginner Contest 117 解题报告

    果然abc都是手速场. 倒序开的qwq. D题因为忘记1e12二进制几位上界爆了一发. A - Entrance Examination 就是除一下就行了... 看样例猜题意系列. #include& ...

  7. AtCoder Beginner Contest 132 解题报告

    前四题都好水.后面两道题好难. C Divide the Problems #include <cstdio> #include <algorithm> using names ...

  8. AtCoder Beginner Contest 129 解题报告

    传送门 写了四个题就跑去打球了.第五题应该能肝出来的. A - Airplane #include <bits/stdc++.h> using namespace std; inline ...

  9. AtCoder Beginner Contest 127 解题报告

    传送门 非常遗憾.当天晚上错过这一场.不过感觉也会掉分的吧.后面两题偏结论题,打了的话应该想不出来. A - Ferris Wheel #include <bits/stdc++.h> u ...

  10. AtCoder Beginner Contest 126 解题报告

    突然6道题.有点慌.比赛写了五个.罚时爆炸.最后一个时间不太够+没敢写就放弃了. 两道题奇奇怪怪的WJ和20/20.今天的评测机是怎么了. A Changing a Character #includ ...

随机推荐

  1. [Oracle]复习笔记-SQL部分内容

    Oracle笔记--SQL部分 整体框架 语句的执行顺序:from →where →group by→having→select→order by select * from * where * gr ...

  2. 7.Gitee导入其他远程托管中心仓库

    的码云是开源中国推出的基于Git的代码托管服务中心 网址是https://gitee.com/,使用方式跟github一致,并且是一个中文网站 码云的使用配置方式与github一致,码云支持导入git ...

  3. 7. url反向解析和静态文件

    一.代码中url出现的位置 1.模版[html]中 1.<a href='urk'>超链接点击跳转<a/> 2.<form action='url' method='po ...

  4. c++ 模板 指针类型偏特化

    一步步来,先简单点. 目标:我们要实现一个模板类,例化后,可以通过get_val获取到值,通过get_ptr获取到指针.具体什么意思结合例子来看看吧. 例子: struct A{ int data; ...

  5. Redis 先操作数据库和先删除缓存, 一致性分析

    初始状态: 数据库和缓存中v=10 第一种,先删除缓存在操作数据库: 线程1准备更新数据库的值v=20,先删除缓存, 此时线程2进来, 缓存未命中,查询数据库v=10, 写入缓存v=10, cpu回到 ...

  6. 京东云开发者|mysql基于binlake同步ES积压解决方案

    1 背景与目标 1.1 背景 国际财务泰国每月月初账单任务生成,或者重算账单数据,数据同步方案为mysql通过binlake同步ES数据,在同步过程中发现计费事件表,计费结果表均有延迟,ES数据与My ...

  7. SQL Server 读写分离配置的一些问题

    1,新建发布服务器遇到此服务器上未安装复制组件 先执行以下sql use mastergoselect @@servername;select serverproperty('servername') ...

  8. Java安全之CC2

    前言 由于在2015年底commons-collections反序列化利⽤链被提出时,Apache Commons Collections有以下两个分⽀版本: commons-collections: ...

  9. 一、Redis的Java客户端

    模糊的目标,要不断去解释它们,把他们转化成一个更具体的内容,这样才能够找到途径. 常用客户端介绍 Jedis客户端 基本使用(直连) 引入对应依赖 <dependency> <gro ...

  10. Spring学习笔记 - 第一章 - IoC(控制反转)、IoC容器、Bean的实例化与生命周期、DI(依赖注入)

    Spring 学习笔记全系列传送门: 目录 1.学习概述 2.Spring相关概念 2.1 Spring概述 2.1.1 Spring能做的工作 2.1.2 重点学习的内容 2.1.3 Spring发 ...