正睿小题

NOIP2022

字符串

回文类

给定一个字符串 a,你每次可以交换 a 的相邻两个字符,问最少进行多少次交换才能使得 a 变为回文串。数据保证一定能够在有限次交换操作后使得 a 变为回文串。保证有解

样例

abcab
1

对于 100%的数据,1≤|a|≤1e6

想到回文串——> 对称性质——>奇数个还是偶数个讨论——>

1. 奇数个去掉中间那个可以变成偶数个。如果是奇数个,那么必然有一个字符是奇数个。把中间的字符替换后就能转化为偶数类。
2. 回文串左右两边是对称的,考虑一边的状态
3. 难点:你需要从交换相邻的两个字符联想到逆序对,可以证明从小到大排是最优策略
证明:在从小到大排的前提下你能想到逆序对的个数就是答案。如果有两个字符是逆序对,那么交换这两个字符,你就能使逆序对的个数减一。考虑两边是对称的。另一半对应的两个字符也必须交换,逆序对的个数可能再减一或者加一。说明这种变换是不劣的。
逆序对用树状数组可以NlogN解决。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int n,a[maxn], c[maxn];
char s[maxn];
vector<int> pos[26];
bool vis[maxn];
void add(int x) {
	while (x <= n) {
		c[x]++;
		x += x & -x;
	}
}
int query(int x) {
	int ret = 0;
	while (x) {
		ret += c[x];
		x -= x & -x;
	}
	return ret;
}
int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for (int i = 1; i <= n; i++){
		pos[s[i] - 'a'].push_back(i);
	}
	if (n & 1) {
		for (int i = 0; i < 26; i++) {
			if (pos[i].size() % 2) {
				int p = pos[i][pos[i].size() / 2];
				a[(n + 1) / 2] = p;
				vis[p] = 1;
			}
		}
	}
	for (int i = 1, j = 1, k = n; i <= n && j <= k; i++) {
		if (vis[i]) {
			continue;
		}//j,k表示双指针,因为是回文。所以一个是从小到大,一个是从大到小。 
		a[j] = i;//这里的手法类似于火柴排队那道题。具体功能看样例。通过数组下标和数值确认是否为逆序对。 
		vis[i] = 1;
		int p = pos[s[i] - 'a'].back();//放第二个 
		a[k] = p;
		vis[p] = 1;
		j++;
		k--;
		pos[s[i] - 'a'].pop_back();
	}
	long long ans = 0;
	for (int i = n; i;--i) {//这里必须倒序,因为回文的后一部分是从大到小。 
		add(a[i]);
		ans += query(a[i] - 1);
	}
	printf("%lld\n", ans);
	return 0;
}
/*
abcab
a[3]=3,vis[3]=1
a[1]=1,vis[1]=1
a[5]=4,vis[4]=1
a[2]=2,vis[2]=1
a[4]=5,vis[5]=1
add(4),query(3)=0
add(5),query(4)=1
······ 
*/

DP

特解类

给定n,求有多少个1~n的排列P满足任意Pi % P(I+1) <= 2,Pn mod P1 <= 2,答案对998244353取模

1.当Pi <Pi+1 时,Pi只能是1或者2.1,2只有两个,可以将这一段数切成两段,以得到两个递减的段。
2.设dp[i]表示填入1~i的方案数。转移时枚举j表示(j,i]在同一个段中且j和i不在同一个段中.如果i<n
  那么要求i+1 mod j <=2 .dp[i]+=dp[j]
3.最终答案为n*dp[n],因为考虑循环移位。                             
4.直接枚举i,j是O(n*n)的,但是我们可以发现i,i-1,i+1中至少有一个是j的倍数,因此我们先枚
举j,然后枚举j的所有倍数转移即可,时间复杂度O(nlogn)。    
/*
3
123
132
213
231
312
321
当n=5时
dp[4]+=dp[3]
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+100,mod=998244353;
int n,f[N];
signed main()
{
	ios::sync_with_stdio(false);
    cin>>n;
    if(n<=3){
    	int res=1;
    	for(int j=1;j<=n;++j)res*=j;
    	cout<<res;
		return 0;
	}
	for(int i=n;i>=3;--i)
	{
		f[i]=1;
		if(i!=3) for(int j=i+1;j<=n;j+=i-1) f[i]+=f[j];
		for(int j=2*i-1;j<=n;j+=i-1) f[i]+=f[j];
		for(int j=2*i-2;j<=n;j+=i-1) f[i]+=f[j];
		f[i]%=mod;
	}
	cout<<f[3]*2*n%mod;
	return 0;
}

posted @ 2022-10-31 16:08  zyc_xianyu  阅读(101)  评论(0编辑  收藏  举报