CF245H Queries for Number of Palindromes(快读+容斥原理 + 记忆化)(好题捏

*题目传送门

分析:

其实这道题关键在于状态转椅上(好像是句废话?)。

一开始的想法就是枚举一个长度 l,计算出两端点 ij,判断一下合并出来的这一段是不是一个回文串,然后有如下的转移方程:

dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+ispar(i,j))

显然它是错的。这个方程没有注意到在合并两个区间的过程中可能会产生新的回文串,并且时间复杂度为 O(n3),是过不了的。

那么如何合理合并两区间时产生的回文串呢?似乎无解了

这里就需要运用到容斥原理了。(我也是第一次见)对于一个端点为 ij 的区间,取

dp[i][j]=dp[i+1][j]+dp[i][j1]

很明显,dp[i+1][j1] 被加了两次,因此只需要减去一次,就可以去重,得到正确答案了。

dp[i][j]=dp[i+1][j]+dp[i][j1]dp[i+1][j1]+ispar(i,j)

多提一句,在判断回文串时,采用传统的 O(n/2) 会超时,用个记忆化即可。

#include<bits/stdc++.h> using namespace std; const int MAXN = 5e3 + 5; int dp[MAXN][MAXN],l,r,t; string s; int par[MAXN][MAXN]; bool check(int l,int r){ if(par[l][r] != -1)return par[l][r]; if(l > r)return 1; if(s[l] == s[r]){ return par[l][r] = check(l + 1,r - 1); } else{ return par[l][r] = 0; } return par[l][r]; } template <class T> void read(T &x){ x=0;char c=getchar();bool f=0; while(!isdigit(c)) f=c=='-',c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); x=f? (-x):x; } int main(){ memset(par,-1,sizeof par); cin >> s; for(int i = 0; i < s.size(); i++){ dp[i][i] = 1; par[i][i] = 1; } for(int l = 1; l < s.size(); l++){ for(int i = 0; i + l < s.size(); i++){ int j = i + l; dp[i][j] = max(dp[i][j],dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1]); if(check(i,j)) dp[i][j]++; } } read(t); while(t--){ read(l);read(r); l--; r--; printf("%d\n",dp[l][r]); } return 0; }

__EOF__

本文作者Never Gonna Give You Up!
本文链接https://www.cnblogs.com/CZ-9/p/16572997.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   腾云今天首飞了吗  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示