YbtOJ#573-后缀表达【二分图匹配】

1|0正题

题目链接:https://www.ybtoj.com.cn/contest/115/problem/2


1|1题目大意

给出一个包含字母变量和若干种同级操作符的后缀表达式。求一个等价的表达式满足该表达式的连续相同段最少。

1|S|2500


1|2解题思路

构建出表达树先,然后看一下什么能够化简,

  1. 两个相邻的相同运算符可以合并
  2. 一个非叶子节点下的相同叶子节点(字母节点)可以合并

先把这些合并了,然后目前的最优解就是现在的节点数量,但是还有一种情况可以合并。

就是兄弟节点中,非叶子节点和叶子节点可以合并。

用类树形dp求出所有节点的子树中的所有表达式的最优答案,如果不考虑上面那种情况就有

ansi=1+x>yansy

然后考虑一个非叶子节点在最优情况下能否以某个字母作为开头,定义avlx,c表示x节点在ans最大的情况下能否以c作为开头。(因为上面那种情况最多剩下一个费用,如果这里牺牲了子树的最优性那么至少需要增加一点费用,显然是一定不优的)

那么对于一个节点的所有儿子,将非叶子节点和叶子节点分成二分图,如果非叶子节点的x满足avlx,c=1,那么向c连边。

然后跑二分图匹配就是ans可以减去的价值。

如何求出avl?如果一个字母cx的儿子那么显然可以作为开头,否则如果有一个字母c满足x的一个非叶子儿子y使得avly,c=1,并且在二分图上删去y节点不会影响答案时,此时将该子树作为开头即可。

如何判断删除一个节点后最大匹配不变,如果原图中该点没有匹配显然可以直接删去。如果有匹配,那么将该节点打上禁止标记后从它的匹配点开始求一条增广路,如果有则可以删去。

时间复杂度O(n2)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<stack> using namespace std; const int N=3100; struct node{ int to,next; }a[N]; int n,cnt,tot,ls[N],ans[N]; bool del[N],leaf[N],ch[N][27],avl[N][27]; char s[N];stack<int> st; namespace M{ node a[N*27];bool v[27]; int tot,ls[27],link[N]; void clear(){ for(int i=1;i<=cnt;i++)link[i]=0; for(int i=1;i<=26;i++)ls[i]=0; tot=cnt=0;return; } void addl(int x,int y){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;return; } int find(int x){ if(v[x])return 0; int p,q;v[x]=1; for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; p=link[y];link[y]=x; if(!p||find(p))return 1; link[y]=p; } return 0; } int Match(int x){ memset(v,0,sizeof(v)); return find(x); } int Path(int x){ if(!link[x])return 1; return Match(link[x]); } } bool isabc(char c) {return (c>='a')&&(c<='z');} void addl(int x,int y){ if(leaf[y])ch[x][s[y]-'a'+1]=1; else{ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot; } return; } void Merge(int x,int y){ for(int i=1;i<=26;i++)ch[x][i]|=ch[y][i]; for(int i=ls[y];i;i=a[i].next) if(!del[a[i].to])addl(x,a[i].to); del[y]=1;return; } void dfs(int x){ for(int i=ls[x];i;i=a[i].next){ int y=a[i].to;dfs(y); if(s[x]==s[y])Merge(x,y); } for(int i=1;i<=26;i++)ans[x]+=ch[x][i]; ans[x]++;return; } void dp(int x){ for(int i=ls[x];i;i=a[i].next) if(!del[a[i].to])dp(a[i].to); M::clear(); for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(del[y])continue; ++cnt;ans[x]+=ans[y]; for(int j=1;j<=26;j++) if(avl[y][j]&&ch[x][j]) M::addl(j,cnt); } for(int i=1;i<=26;i++) if(ch[x][i])ans[x]-=M::Match(i); for(int i=1;i<=26;i++)avl[x][i]=ch[x][i]; for(int i=ls[x],p=0;i;i=a[i].next){ int y=a[i].to; if(del[y])continue;p++; if(M::Path(p)){ for(int j=1;j<=26;j++) avl[x][j]|=avl[y][j]; } } return; } int main() { freopen("expr.in","r",stdin); freopen("expr.out","w",stdout); scanf("%s",s+1); int l=strlen(s+1); for(int i=1;i<=l;i++){ ++n; if(isabc(s[i]))leaf[n]=1; else{ addl(n,st.top());st.pop(); addl(n,st.top());st.pop(); } st.push(n); } dfs(n); dp(n); printf("%d\n",ans[n]); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14407412.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(48)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示