[题解]AtCoder Beginner Contest 406(ABC406) A~F
因为上线时间有限所以补题和题解都延误了(^^;
写得很急,如有错误/不清晰的点请在评论区提出。
A - Not Acceptable
见代码。时间复杂度\(O(1)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a,b,c,d;
signed main(){
cin>>a>>b>>c>>d;
if(a>c) cout<<"Yes\n";
else if(a==c&&b>d) cout<<"Yes\n";
else cout<<"No\n";
return 0;
}
B - Product Calculator
设当前屏幕上的数字为\(fac\),要乘的数是\(a\),则:
如果\(fac\times a\ge 10^k\),则将\(fac\)置为\(1\);否则将\(fac\)累乘\(a\)。
\(fac\times a\)可能会爆long long,可以使用__int128,或者可以转化一下条件:
- \(fac\times a\ge 10^k\iff a\ge \lceil\frac{10^k}{fac}\rceil\)。
时间复杂度\(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,fac=1;
signed main(){
cin>>n>>k;
int t=1;
for(int i=1;i<=k;i++) t*=10;
while(n--){
int a;
cin>>a;
if(a>=(t-1)/fac+1) fac=1;
else fac*=a;
}
cout<<fac<<"\n";
return 0;
}
C - ~
显然峰值和谷值是交替出现的。
我们要找到的就是一个区间,使得这个区间:
- 恰好有\(1\)个峰值和\(1\)个谷值。
- 峰值在谷值前。
- 区间头尾均不是峰值/谷值。
定义\(nxt[i]\)为\(a[i\sim n]\)中第一个峰/谷的位置。
枚举所有\(i\)使得\(a[i]<a[i+1]\),定义\(n_1=nxt[i+1],n_2=nxt[n_1+1],n_3=nxt[n_2+1]\)。
如果\(n_1,n_2\)都存在,则:
- 如果\(n_3\)存在,则累加\(n_3-n_2\)的贡献。
- 如果\(n_3\)不存在,则累加\(n-n_2\)的贡献。
时间复杂度\(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 300010
using namespace std;
int n,a[N],b[N],nxt[N],pre[N],ans;
signed main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=2;i<n;i++) if((a[i]>a[i-1]&&a[i]>a[i+1])||(a[i]<a[i-1]&&a[i]<a[i+1])) b[i]=1;
for(int i=1;i<=n;i++) pre[i]=(b[i]?i:pre[i-1]);
for(int i=n;i>=1;i--) nxt[i]=(b[i]?i:nxt[i+1]);
for(int i=1;i<n;i++){
if(a[i]<a[i+1]){
int n1=nxt[i+1],n2=nxt[n1+1],n3=nxt[n2+1];
if(n1&&n2) ans+=(n3?n3-n2:n-n2);
}
}
cout<<ans<<"\n";
return 0;
}
D - Garbage Removal
-
赛时思路(稍有修改):为行和列各开一个
set存里面的垃圾的坐标\((x,y)\),每处理一个行的操作,就遍历该行的垃圾,并在其所在列中删除它。列同理。为了方便查找,代码将\((x,y)\)压成一个整数了。
时间复杂度\(O(n+m+k+q)\)。
-
题解思路:同一行/列的询问,从第\(2\)次开始就是\(0\)不变了。所以为行和列各开一个桶来统计该行/列是否被查询过,如果查询过直接输出\(0\),否则遍历该行/列的垃圾,如果没有被清除,则产生\(1\)的贡献,并标记该垃圾已被清除。
由于每行、每列的垃圾最多被遍历\(1\)次,所以时间复杂度为\(O(n+m+k+q)\)。
赛时思路
#include<bits/stdc++.h>
#define int long long
#define N 200010
using namespace std;
int n,m,k,q;
unordered_map<int> hang[N],lie[N];
signed main(){
cin>>n>>m>>k;
while(k--){
int x,y;
cin>>x>>y;
hang[x].insert(x*N+y),lie[y].insert(x*N+y);
}
cin>>q;
while(q--){
int op,x;
cin>>op>>x;
if(op==1){
cout<<hang[x].size()<<"\n";
for(auto i:hang[x]) lie[i%N].erase(i);
hang[x].clear();
}else{
cout<<lie[x].size()<<"\n";
for(auto i:lie[x]) hang[i/N].erase(i);
lie[x].clear();
}
}
return 0;
}
题解代码见https://atcoder.jp/contests/abc406/editorial/13053。
E - Popcount Sum 3
数位dp,下文使用DFS实现,规定最低位为\(1\)。
由于要对满足条件的数求和,所以如果我们搜索到最后再统计贡献的话,将很难记忆化。因为就算两个搜索的状态“当前的数位”和“到目前为止\(1\)的个数”都相同,由于已经填好的数位不同,两个状态的答案也一定不同。但是这两个状态往后填写的情况是完全相同的。
所以我们不妨修改策略,让每个状态仅统计:
- \(sum\):其搜索子树中所有答案 从当前数位到最低位的子串 之和。
- \(cnt\):其搜索子树中的答案个数。
\(sum\)的定义说明每个状态仅需关注它后面所填的值,不需要管之前填写的内容。与CF1073E Segment Sum(题解)这道题非常相似,可以类比一下两题的做法。
假设当前填到第\(pos\)位,则有转移:
最后输出\(sum[root]\)即可。
可以使用\(f[pos][pc]\)来记忆化,其中\(pc\)是填到目前\(1\)的个数。
时间复杂度\(O(|\Sigma|\times \lg n\times k)\)。其中\(|\Sigma|=2\),表示字符集大小。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define K 70
using namespace std;
int t,n,k,a[K];
bitset<K> fv[K];
pair<int,int> f[K][K];
//first表示cnt,second表示sum
pair<int,int> dfs(int pos,bool limit,int pc){
if(pc>k) return {0,0};
if(!pos) return {(pc==k),0};
if(!limit&&fv[pos][pc]) return f[pos][pc];
int rig=limit?a[pos]:1;
pair<int,int> ans{0,0};
for(int i=0;i<=rig;i++){
auto res=dfs(pos-1,limit&&i==rig,pc+i);
(ans.second+=res.second)%=mod;
(ans.second+=i*(1ll<<(pos-1))%mod*res.first%mod)%=mod;
(ans.first+=res.first)%=mod;
}
if(!limit) fv[pos][pc]=1,f[pos][pc]=ans;
return ans;
}
int solve(int x){
for(int i=0;i<K;i++) fv[i]=0;
int len=0;
while(x) a[++len]=x&1,x>>=1;
return dfs(len,1,0).second;
}
signed main(){
cin>>t;
while(t--){
cin>>n>>k;
cout<<solve(n)<<"\n";
}
return 0;
}
F - Compare Tree Weights
核心操作在于单点修改权值以及查询子树和。
我们按DFS序给每个节点重新编号,容易发现一个子树中节点的DFS序是连续的,所以可以放到树状数组/线段树上操作。
其实就是把树剖模板题里“子树操作”弱化后搬到这来了。
代码的时间复杂度\(O((n+q)\log n)\),树状数组初始化优化一下可以做到\(O(n+q\log n)\)。
点击查看代码
#include<bits/stdc++.h>
#define N 300010
using namespace std;
int n,q,tim,dfn[N],siz[N],u[N],v[N],dep[N];
vector<int> G[N];
inline int lowbit(int x){return x&-x;}
struct BIT{
int s[N];
void add(int x,int k){while(x<=n) s[x]+=k,x+=lowbit(x);}
int query(int x){int ans=0;while(x) ans+=s[x],x-=lowbit(x);return ans;}
int query(int l,int r){return query(r)-query(l-1);}
}bit;
void dfs(int u,int fa){
dfn[u]=++tim,siz[u]=1,dep[u]=dep[fa]+1;
for(int i:G[u]) if(i!=fa) dfs(i,u),siz[u]+=siz[i];
}
void add(int u,int v){G[u].emplace_back(v);}
signed main(){
cin>>n;
for(int i=1;i<n;i++) cin>>u[i]>>v[i],add(u[i],v[i]),add(v[i],u[i]);
dfs(1,0);
for(int i=1;i<n;i++) if(dep[u[i]]<dep[v[i]]) swap(u[i],v[i]);
for(int i=1;i<=n;i++) bit.add(i,1);
cin>>q;
while(q--){
int o,x,y;
cin>>o;
if(o==1){
cin>>x>>y,bit.add(dfn[x],y);
}else{
cin>>x;
int s=bit.query(n),t=bit.query(dfn[u[x]],dfn[u[x]]+siz[u[x]]-1);
cout<<abs(2*t-s)<<"\n";
}
}
return 0;
}
浙公网安备 33010602011771号