Soratosorato

ABC359D&E&G 题解

Sorato·2024-06-27 08:26·50 次阅读

ABC359D&E&G 题解

ABC359D Avoid K Palindrome#

题目大意#

给定一个长度为 N 的字符串 S,由 AB? 组成。可以在 ? 处任意填 AB。再给定一个常数 K,问有多少种填法可以使 S 的每一个长度为 K 的子串都不是回文串,即成为一个好串。

Solve#

注意到 K10,考虑暴力 DFS 或状压 DP。由于状态转移比较直观,这里用状压。

dpi,s 表示 S 的前 i 位是好串,以 i 结尾的长度为 K 的子串状态为 s 的方案数。s 二进制下的第 j 位为 1 表示 S 对应的这一位是 A,否则是 B

对于遍历到的 i,枚举第 i1 位的状态 now,进行状态转移。

  • SiA?,则第 i 位的状态 nxtnow 删去最左侧的一位,左移一位,再将对应 Si 的最右侧的一位变为 1,二进制表示为 now<<1&(1<<k)-1|1
  • SiB?,则第 i 位的状态 nxtnow 删去最左侧的一位,左移一位,二进制表示为 now<<1&(1<<k)-1

nxt 不是回文,则令 dpi,nxt 加上 dpi1,now

Code#

Copy
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { short f=1; int x=0; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define mod 998244353 int n,m,dp[1010][1024]={1},res,ans,k; char s[1010]; bool f[1024];//预处理出哪些状态不是回文串 signed main() { n=read();m=1<<(k=read()); scanf("%s",s+1); for(int now=0;now<m;now=-~now) { f[now]=0; for(int i=0;i<(k>>1);i=-~i) if((now>>i&1)^(now>>(k-1-i)&1)) { f[now]=1; break; } } for(int i=1;i<=n;i=-~i) for(int now=0;now<m;now=-~now) { int nxt=now<<1&m-1; if(s[i]=='A'||s[i]=='?') if(i<k||f[nxt|1])//i<k时特判 dp[i][nxt|1]=(dp[i][nxt|1]+dp[i-1][now])%mod;//注意取模 if(s[i]=='B'||s[i]=='?') if(i<k||f[nxt]) dp[i][nxt]=(dp[i][nxt]+dp[i-1][now])%mod; } for(int now=0;now<m;now=-~now) ans=(ans+dp[n][now])%mod; return printf("%lld",ans),0; }

ABC359E Water Tank#

题目大意#

给定一个长度为 N 的序列 H,和一个长度为 N+1 初始为 0 的序列 A。每一轮在 A 上进行如下操作:

  • A0 的值增加 1
  • 依次遍历 1N,若 Ai1>AiAi1>Hi,则将 Ai11,将 Ai1

求对于 i=1,2,,N,需要多少轮操作能使 Ai>0

Solve#

如果要使 A1>0,首先要将 A0 填满,耗费 3,再向 A11

如果要使 A2>0,要将 H2 左侧的都填满,共耗费 12

如果要使 A5>0,要将 H5 左侧的都填满,共耗费 40

以此类推,若 Hi 是前缀最大值,那么就要耗费 i×Hi 的代价把 Hi 左侧全填满,也就是填成一个矩形。

那如果 Hi 不是前缀最大值呢?以 H4 为例。

忽略墙的厚度,则相当于把 H2H4 间填成一个矩形,共耗费 H4×(42)

以此类推,若 Hi 不是前缀最大值,那么需要去找 Hi 左侧第一个比它高的 Hj,花费即为 Hi×(ij)

单调栈实现之。

Code#

Copy
// LUOGU_RID: 163040327 #include<bits/stdc++.h> using namespace std; #define int long long inline int read() { short f=1; int x=0; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,h[200010],sum[200010]; stack<int>s; signed main() { n=read(); for(int i=1;i<=n;i=-~i) { h[i]=read(); while(!s.empty()&&h[s.top()]<=h[i]) s.pop(); if(s.empty()) sum[i]=i*h[i]; else sum[i]=sum[s.top()]+(i-s.top())*h[i]; printf("%lld ",sum[i]+1); s.push(i); } return 0; }

ABC359G Sum of Tree Distance#

题目大意#

给定一棵 N 个节点树,和每个点的权值 Ai。求 i=1N1j=i+1Ndis(i,j)×[Ai=Aj]

Solve#

点分治板子啊。

对于分治出的以 u 为根的子树,令 dep[u]0dep 是子树中每个点的深度,也就是到根节点的距离。

sumi 统计所有点权为 i 的点到子树的根的距离之和,再用 cnti 统计所有点权为 i 的点的个数,当遍历到点 v 时,让答案加上 sumAv+cntAv×depv,也就是加上之前遍历过的点到 v 点的距离和。然后在遍历完这一分支之后修改 sumcnt 数组以实现容斥即可,没啥细节。

Code#

Copy
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { short f=1; int x=0; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,v[200010],ans; vector<int>e[200010]; int mn,root,siz[200010]; int dep[200010],sum[200010],now,cnt[200010]; bool vis[200010]; vector<int>a,b; void get_siz(int u,int fa) { siz[u]=1; int mx=0; for(auto i:e[u]) if(i!=fa&&!vis[i]) get_siz(i,u), mx=max(mx,siz[i]),siz[u]+=siz[i]; mx=max(mx,now-siz[u]); if(mx<mn) mn=mx,root=u; } inline int get_rt(int u)/*找重心*/ { mn=n;now=siz[u]; get_siz(u,0); return root; } void get_dis(int u,int fa) { a.push_back(u); for(auto i:e[u]) if(i!=fa&&!vis[i]) dep[i]=-~dep[u], get_dis(i,u); } inline void calc(int u) { dep[u]=1; a.clear(); get_dis(u,0); for(auto i:a) ans+=sum[v[i]]+dep[i]*cnt[v[i]]; } void solve(int u) { vis[u]=1; cnt[v[u]]=-~cnt[v[u]];/*别忘了算上子树的根*/ for(auto i:e[u]) if(!vis[i]) { calc(i); for(auto i:a) sum[v[i]]+=dep[i],cnt[v[i]]=-~cnt[v[i]], b.push_back(i); } for(auto i:b) sum[v[i]]-=dep[i],cnt[v[i]]--; b.clear();cnt[v[u]]--; for(auto i:e[u]) if(!vis[i]) solve(get_rt(i)); } signed main() { siz[1]=n=read(); for(int i=1,a,b;i<n;i=-~i) a=read(),b=read(), e[a].push_back(b),e[b].push_back(a); for(int i=1;i<=n;i=-~i) v[i]=read(); solve(get_rt(1)); return printf("%lld",ans),0; }

(点分治的写法可能有些丑,凑合着看吧。)

posted @   Sorato  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录