[USACO22DEC] Palindromes P 题解

T3 [USACO22DEC] Palindromes P

郝题。首先考虑给定一个串 \(S\) 怎么求出要换多少次。

易得,不可能交换两个本来就相同的字符。不妨观察 \(\texttt G\) 的回文关系,一对 \(\texttt G\) 回文当且仅当第一个 \(\texttt G\) 前面的 \(\texttt H\) 数量等于第二个 \(\texttt G\) 后面的 \(\texttt H\) 的数量。换句话说,若当前的串 \([l,r]\),其中有一对 \(\texttt G\) 的位置为 \((a,b)\),则将这一对 \(\texttt G\) 归位的花费为:

\[\big|(a+b)-(l+r)\big| \]

简单手模应该不难得到。要注意特殊情况:

  • 若串长为偶数但 \(\texttt G\) 的个数为奇数,不可能为回文串,\(-1\) 走人;
  • 否则一定存在一个 \(\texttt G\) 在正中间的位置,计算 \(\Big|\mathit{pos}-\big\lfloor(l+r)/2\big\rfloor\Big|\)

所以我们就有一个 \(O(n^3)\) 的暴力,其中 \(O(n^2)\) 枚举子串,\(O(n)\) 计算答案。类似一个区间 DP 的复杂度,由于跑不满,可以通过 \(n\le2000\) 的数据,实测 36pts。

一般来说想到这里就差不多了。

我们发现可以转变转移方式,由中间向两边扩展。先枚举 \(i\) 作为中点,神奇地用树状数组维护 \(l+r\) 的值统计答案,然后向左向右扩展之后,再将新的 \(l+r\) 扔进树状数组。注意到原来有绝对值的符号,所以还需要维护 \(l+r\) 和的个数,算出 \(<l+r\)\(>l+r\) 的个数,就是一个 \(O(n^2\log n)\) 的做法。题解区还有 \(O(n^2)\) 的做法。

感觉重点还是在于发现这个式子。

#include<bits/stdc++.h>
#define lowbit(x) (x&-x)
using namespace std;

using ll=long long;
constexpr int MAXN=7505;
int t[MAXN],n;
string s;
struct BIT{
	ll c[MAXN<<1];
	void add(int x,int k){
		while(x<=n<<1)c[x]+=k,x+=lowbit(x);
	}
	ll sum(int x){
		ll res=0;
		while(x)res+=c[x],x-=lowbit(x);
		return res;
	}
	void clear(){
		memset(c,0,sizeof(c));
	}
}t1,t2;

int main(){
	ios::sync_with_stdio(0);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>s;
	n=s.size();
	s=' '+s;
	int c=0;
	for(int i=1;i<=n;++i)if(s[i]=='G')t[++c]=i;
	t[c+1]=n+1;
	ll ans=0,sum=0,lans=0;
	for(int i=1;i<=c;++i){
		int lf=i,rf=i;
		while(lf&&rf<=c){
			int fk=t[lf]+t[rf];
			if(lf^i){
				t1.add(fk,fk),t2.add(fk,1);
				sum+=fk;
				++lans;
			}
			for(int l=t[lf];l>t[lf-1];--l)
				for(int r=t[rf];r<t[rf+1];++r){
					if((r-l+1)%2==0){
						--ans;
						continue;
					}
					ll p=t2.sum(l+r-1),q=t1.sum(l+r-1);
					ans+=abs((l+r)/2-t[i]);
					ans+=(p*(l+r)-q)+(sum-q)-(lans-p)*(l+r);
				}
			--lf,++rf;
		}
		sum=lans=0;
		t1.clear(),t2.clear();
	}
	for(int i=1;i<c;++i){
		int lf=i,rf=i+1;
		while(lf&&rf<=c){
			int fk=t[lf]+t[rf];
			t1.add(fk,fk),t2.add(fk,1);
			sum+=fk;
			++lans;
			for(int l=t[lf];l>t[lf-1];--l)
				for(int r=t[rf];r<t[rf+1];++r){
					ll p=t2.sum(l+r-1),q=t1.sum(l+r-1);
					ans+=(p*(l+r)-q+(sum-q)-(lans-p)*(l+r));
				}
			--lf,++rf;
		}
		sum=lans=0;
		t1.clear(),t2.clear();
	}
	cout<<ans<<'\n';
	return 0;
}
posted @ 2024-09-29 19:42  Laoshan_PLUS  阅读(22)  评论(0)    收藏  举报