感受有趣的一道计数,实际上不难c++
感受很久没用这种技巧了,致使我还在错误的道路上想了很久。。。spa
观察题目性质,能够发现就是左边第一次出现两遍的数字的位置\([i,n]\)是一个合法的次级子串code
右边第一次出现两遍的数字的位置\([1,j]\)是一个合法的次级子串,两边取maxget
且最长次级子串必定和首尾相连,至少出现两遍的子序列必定是次级子串的最后一个字符出现两遍和它的前\(|s| - 1\)个字符构成的string
这道题就是用至多长度为\(n\)的减去至多长度为\(n - 1\)的it
因此咱们如今考虑如何算至多长度为\(n\)的class
首先若是长度小于等于\(n+1\)的串,必定次级子串长度至多为\(n\),这是一个等比数列求和date
而后考虑长度为\(n + 1 + i\)的串,很容易发现\(i <= K\),由于一旦超过\(K\),前\(n + 1\)个字符的后面就会出现至少两个相同的字母技巧
那么分两种状况考虑,一种是\(n + 1 > i\)db
那么这个\(n + 1 + i\)的串前\(n + 1 - i\)个和后\(n + 1 - i\)个都必须有限制,第一条是左边的数两两不一样,右边的数两两不一样,左边限制是不能和左数第\(n + 2 - i\)个相同,右边限制是不能和右数第\(n + 2 - i\)个相同。
这个组合数算一算就行了,不难
另外一种状况是\(n + 1 <= i\)
那么就是前\(i + 1\)个必须两两不一样,而后后\(n\)个也要两两不一样,可是后\(n\)个能够和前\(n\)个相等,和\([n + 1,i + 1]\)不相等,这也是简单的排列组合
总之就是想到至多的那个容斥基本就会了……
#include <bits/stdc++.h> #define fi first #define se second #define pii pair<int,int> #define mp make_pair #define pb push_back #define space putchar(' ') #define enter putchar('\n') #define eps 1e-10 #define ba 47 #define MAXN 200005 //#define ivorysi using namespace std; typedef long long int64; typedef unsigned int u32; typedef double db; template<class T> void read(T &res) { res = 0;T f = 1;char c = getchar(); while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { res = res * 10 +c - '0'; c = getchar(); } res *= f; } template<class T> void out(T x) { if(x < 0) {x = -x;putchar('-');} if(x >= 10) { out(x / 10); } putchar('0' + x % 10); } const int MOD = 998244353; int fac[MAXN],invfac[MAXN]; int N,K; int inc(int a,int b) { return a + b >= MOD ? a + b - MOD : a + b; } int mul(int a,int b) { return 1LL * a * b % MOD; } void update(int &x,int y) { x = inc(x,y); } int C(int n,int m) { if(n < m) return 0; else return mul(fac[n],mul(invfac[m],invfac[n - m])); } int fpow(int x,int c) { int res = 1,t = x; while(c) { if(c & 1) res = mul(res,t); t = mul(t,t); c >>= 1; } return res; } int getsum(int n,int q) { if(n <= 0) return 0; if(q == 1) return n; int t = inc(1,MOD - fpow(q,n + 1)); t = mul(t,fpow(1 + MOD - q,MOD - 2)); update(t,MOD - 1); return t; } int query(int n) { if(n == 0) return 0; int res = getsum(n,K); for(int i = 1 ; i <= K ; ++i) { int tmp = 0; if(n > i) { update(tmp,fpow(K,n - i)); int t = mul(C(K - 1,i),fac[i]); t = mul(t,t); tmp = mul(tmp,t); } else if(n <= i) { int t = mul(C(K,i + 1),fac[i + 1]); int h = mul(C(K - (i - n + 2),n - 1),fac[n - 1]); update(tmp,mul(t,h)); } update(res,tmp); } return res; } void Solve() { read(N);read(K); out(inc(query(N + 1),MOD - query(N)));enter; } int main(){ #ifdef ivorysi freopen("f1.in","r",stdin); #endif fac[0] = 1; for(int i = 1 ; i <= 200000 ; ++i) fac[i] = mul(fac[i - 1],i); invfac[200000] = fpow(fac[200000],MOD - 2); for(int i = 199999 ; i >= 0 ; --i) invfac[i] = mul(invfac[i + 1],i + 1); int T; read(T); for(int i = 1 ; i <= T ; ++i) Solve(); }