Codeforces 884F - Anti-Palindromize (费用流)
题意
定义一个字符串\(S\)是反回文的,当且仅当该字符串长度为\(m\)时,对于任意的\(i\in[1,m]\),满足\(S_i \neq S_{m-i+1}\)
现在给定一个字符串\(S\),假设长度为\(m\),\(m\)一定为偶数
你可以随意交换字符串\(S\)中的字母顺序,使其成为一个新字符串\(T\)(换言之,\(T\)是\(S\)的排列),且\(T\)串是一个反回文串
那么此时字符串\(T\)的漂亮值计算方法为:对于位置\(i\in[1,m]\),如果\(S_i=T_i\),则漂亮值加上\(b_i\)
问最大的漂亮值
限制
Time limit per test: 2 seconds
Memory limit per test: 256 megabytes
\(2\leq n\leq 100\)
\(1\leq b_i\leq 100\)
思路
根据题目对于“反回文串”的描述,可以得知
对于某个\(i\in[1,\frac m 2]\),\(T_i\neq T_{m-i+1}\)必须成立
由于构造某个字符串的新排列只需要严格保证字符出现的个数相等即可
那么这就变成了一道匹配问题,对于可能构造出来的\(T\)串每个位置的字符进行匹配
由于又引入了\(\{b\}\)这样的贡献数组,即如果\(S_i=T_i\)时对答案贡献就会加上\(b_i\),就可以考虑采用费用流来做
首先,每个位置只能代表一个字符,所以从源点连向每个位置,限制流量为\(1\)
再考虑这是一个排列,所以要限制字符的出现次数,所以将每种字符对应的点连向汇点,限制流量为\(S\)串中出现的次数
于是就该考虑每个位置的匹配情况了
将每个位置所有可能的情况列出
首先考虑到\(T_i\neq T_{m-i+1}\)的限制,枚举字符种类\(k\),将\(i\)与\(m-i+1\)合为一组表示成一个单独的点,设该点为\(x\)
则\(x\)的流通状态表示\(i\)或\(m-i+1\)这两个位置中有一个匹配上了字符\(k\)
所以应该从\(i\)与\(m-i+1\)两个位置连向点\(x\),流量为\(1\),但费用需要取决于对应位置在原串中的字符是否为\(k\)
两个点只能有一个匹配上,所以从点\(x\)连向字母\(k\),限制流量为\(1\)
这样全图在\(m=100\)的情况下,表示位置的点有\(100\)个,表示字母的点有\(26\)个,超级源点汇点\(2\)个,枚举各组匹配的字符种类对应的点有\(50*26=1300\)个,对应好下标后跑费用流即可
代码
(46ms/2000ms)
/*
* Author : StelaYuri
* Language : GNU G++ 14
*/
//#pragma GCC optimize(3)
#include<bits/stdc++.h>
//#include<unordered_map>
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/hash_policy.hpp>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define pb push_back
#define eb emplace_back
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const double angcst=PI/180.0;
const ll mod=998244353;
ll max_3(ll a,ll b,ll c){if(a>b&&a>c)return a;if(b>c)return b;return c;}
ll min_3(ll a,ll b,ll c){if(a<b&&a<c)return a;if(b<c)return b;return c;}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}
const int maxn=6666;
struct MCMF {
struct E {
int from, to, cap, v;
E() {}
E(int f, int t, int cap, int v) : from(f), to(t), cap(cap), v(v) {}
};
int n, m, s, t;
vector<E> edges;
vector<int> G[maxn];
bool inq[maxn];
int dis[maxn], pre[maxn], a[maxn];
void init(int _n, int _s, int _t) {
n = _n; s = _s; t = _t;
for (int i = 0; i <= n; i++)
G[i].clear();
edges.clear();
m = 0;
}
void add(int from, int to, int cap, int cost) {
edges.emplace_back(from, to, cap, cost);
edges.emplace_back(to, from, 0, -cost);
G[from].push_back(m++);
G[to].push_back(m++);
}
bool spfa() {
for (int i = 0; i <= n; i++) {
dis[i] = 1e9;
pre[i] = -1;
inq[i] = false;
}
dis[s] = 0, a[s] = 1e9, inq[s] = true;
queue<int> Q; Q.push(s);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;
for (int& idx: G[u]) {
E& e = edges[idx];
if (e.cap && dis[e.to] > dis[u] + e.v) {
dis[e.to] = dis[u] + e.v;
pre[e.to] = idx;
a[e.to] = min(a[u], e.cap);
if (!inq[e.to]) {
inq[e.to] = true;
Q.push(e.to);
}
}
}
}
return pre[t] != -1;
}
int solve() {
int flow = 0, cost = 0;
while (spfa()) {
flow += a[t];
cost += a[t] * dis[t];
int u = t;
while (u != s) {
edges[pre[u]].cap -= a[t];
edges[pre[u] ^ 1].cap += a[t];
u = edges[pre[u]].from;
}
}
return cost;
}
}f;
int b[111];
int cnt[30];
void solve()
{
int len;
string str;
cin>>len>>str;
str=' '+str; //移位
rep(i,1,len)
{
cin>>b[i];
cnt[str[i]-'a']++;
}
f.init(1428,1427,1428);
rep(i,1,len)
f.add(f.s,i,1,0); //源点连向每个位置
rep(i,0,25)
f.add(i+1401,f.t,cnt[i],0); //每个字母连向汇点
rep(i,0,25) //枚举字符种类
{
rep(j,1,len/2) //枚举对应的组别
{
int tmp=100+i*(len/2)+j;
f.add(tmp,1401+i,1,0); //连向对应字母,限制流量
if(i==str[j]-'a') //如果与指定位置的字母相同,那么加入贡献中,下同
f.add(j,tmp,1,-b[j]);
else
f.add(j,tmp,1,0);
if(i==str[len-j+1]-'a')
f.add(len-j+1,tmp,1,-b[len-j+1]);
else
f.add(len-j+1,tmp,1,0);
}
}
cout<<-f.solve()<<'\n';
}
int main()
{
closeSync;
//multiCase
{
solve();
}
return 0;
}