1. 程式人生 > 其它 >題解 拋硬幣

題解 拋硬幣

傳送門

這題真的啥也不會……
@Yubai賽時隨手切了,Orz

留個坑,自動機啥也不會……
這裡其實有個很套路的DP柿子 \(dp[i][j] = dp[i-1][j]+dp[i-1][j-1]\)
即為分別考慮位置i上的數選或不選所造成的貢獻
但是這裡還有個重複情況需要考慮

1 2 3 4 5 6 7 8
a b c d e a b c
這裡a與位置2上的b和位置7上的b會重複造成貢獻(重複的字首)
所以需要減去,令\(p[i]\)記錄上一個s[i]出現的位置
所以最終方程為

\[dp[i][j] = dp[i-1][j]+dp[i-1][j-1]-dp[p[i]-1][j-1] \]

注意這裡有個減一,因為是減去字首的重複

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 3050
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, l, cnt[N];
const ll p=998244353;
char s[N];

namespace force{
	ll ans;
	const ll base=131;
	unordered_map<ll, bool> mp;
	void dfs(int u, int l, ll h) {
		//cout<<"dfs "<<u<<' '<<l<<endl;
		if (l<=0) {
			//cout<<h<<endl;
			if (!mp[h]) {++ans; mp[h]=1;}
			return ;
		}
		if (u>n) return ;
		dfs(u+1, l, h);
		dfs(u+1, l-1, (h*base+s[u])%p);
	}
	void solve() {
		//for (int i=0; i<=n; ++i) cout<<cnt[i]<<' '; cout<<endl;
		dfs(1, l, 0);
		printf("%lld\n", ans%p);
		exit(0);
	}
}

namespace task{
	ll lst[26], p[N], dp[N][N];
	const ll mod=998244353;
	void solve() {
		for (int i=1; i<=n; ++i) {
			p[i]=lst[s[i]-'a'];
			lst[s[i]-'a']=i;
		}
		dp[0][0]=1;
		for (int i=1; i<=n; ++i) 
			for (int j=0; j<=l; ++j) 
				dp[i][j] = (dp[i-1][j]+dp[i-1][j-1]-(p[i]?dp[p[i]-1][j-1]:0))%mod;
		printf("%lld\n", (dp[n][l]+mod)%mod);
		exit(0);
	}
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	scanf("%s%d", s+1, &l); n=strlen(s+1);
	if (n==l) {puts("1"); return 0;}
	for (int i=2; i<=n; ++i) if (s[i]!=s[i-1]) goto jump;
	puts("1"); return 0; jump:
	for (int i=1; i<=n; ++i) ++cnt[s[i]-'a'];
	if (l==1) {
		int ans=0;
		for (int i=0; i<26; ++i) if (cnt[i]) ++ans;
		printf("%d\n", ans); return 0;
	}
	//force::solve();
	task::solve();

	return 0;
}