題解 拋硬幣
阿新 • • 發佈:2021-07-16
這題真的啥也不會……
@Yubai賽時隨手切了,Orz
留個坑,自動機啥也不會……
這裡其實有個很套路的DP柿子 \(dp[i][j] = dp[i-1][j]+dp[i-1][j-1]\)
即為分別考慮位置i上的數選或不選所造成的貢獻
但是這裡還有個重複情況需要考慮
\[dp[i][j] = dp[i-1][j]+dp[i-1][j-1]-dp[p[i]-1][j-1] \]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]出現的位置
所以最終方程為
注意這裡有個減一,因為是減去字首的重複
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; }