题解 Sequence

传送门

只会爆搜系列

  • 关于「本质不同的子序列个数」:限定长度无限制(就是这题)
    无限制的柿子是(令 \(dp[i]\) 为以 \(i\) 为结尾的不同子序列个数) \(dp[i] = \sum dp[j]+1\),代表在所有子序列末尾后面接上这个字母,且它自身也是一个子序列

然后这题还可以填上 \(m\) 个数,并要求最大化方案数
有个我没想到的贪心,每次填方案数最少的那个字母
因为根据上面的转移,无论这一次选哪个字母,它们的dp值都是一样的
于是发现我们填的数其实是 \(k\) 的一个排列
然后 \(m\) 很大而 \(k\) 只有 \(100\),考虑矩阵快速幂
每 \(k\) 次下来整体的转移是固定的,可以建出系数矩阵
剩下不足 \(k\) 的地方暴力处理就好了

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f
#define N 1000010
#define ll long long
#define ld long double
#define ull unsigned long long 
#define fir first
#define sec second
#define make make_pair
#define reg register int
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline ll read() {
	ll 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, k; ll m;
int a[N];
const ll mod=1e9+7;
int mod2=1e9+7;
//inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}
inline void md2(int& a, int b) {a+=b; a=a>=mod2?a-mod2:a;}
const ull base=131;
unordered_map<ull, bool> mp, mp2;

namespace task1{
	void dfs(int u, ull dat) {
		dat=dat*base+a[u];
		mp[dat]=1;
		for (int i=u+1; i<=n; ++i) {
			dfs(i, dat);
		}
	}
	void solve() {
		for (int i=1; i<=n; ++i) dfs(i, 0);
		cout<<mp.size()%mod<<endl;
		exit(0);
	}
}

namespace task2{
	ull sta[N]; int top; unsigned ans;
	void dfs(int u, unordered_map<ull, bool> tmp) {
		//cout<<"dfs "<<u<<endl;
		if (u>m) {ans=max(ans, tmp.size()); return ;}
		top=0; ull tem;
		for (int j=1; j<=k; ++j) {
			//cout<<"j: "<<j<<endl;
			unordered_map<ull, bool> tp2=tmp;
			for (auto it:tmp) {
				tem=it.fir;
				tp2[tem*base+j]=1;
			}
			tp2[j]=1;
			dfs(u+1, tp2);
		}
	}
	void solve() {
		for (int i=1; i<=n; ++i) task1::dfs(i, 0);
		dfs(1, mp);
		cout<<ans<<endl;
	}
}

namespace task3{
	ll dp[110]; ld dp2[110];
	int sta[110], top;
	void solve() {
		ll sum; ld sum2;
		for (int i=1; i<=n; ++i) {
			sum=0; sum2=0;
			for (int j=1; j<=k; ++j)
				md(sum, dp[j]), sum2+=1.0*dp[j];
			dp[a[i]]=sum, dp2[a[i]]=sum2+1.0;
			md(dp[a[i]], 1);
		}
		for (int i=1,pos=0; i<=m; ++i,pos%=k) {
			if (top==k) {
				sum=0; sum2=0;
				for (int j=1; j<=k; ++j)
					md(sum, dp[j]), sum2+=dp[j];
				dp[sta[pos]]=sum; dp2[sta[pos]]=sum2+1;
				md(dp[sta[pos++]], 1);
			}
			else {
				//ll minn=INF;
				ld minn=1e1000l; int mini=0; sum=0; sum2=0;
				for (int j=1; j<=k; ++j) {
					if (dp2[j]<minn) minn=dp2[j], mini=j;
					md(sum, dp[j]), sum2+=dp2[j];
				}
				dp[mini]=sum; dp2[mini]+=sum2;
				md(dp[mini], 1);
				sta[top++]=mini;
			}
		}
		sum=0;
		for (int i=1; i<=k; ++i) md(sum, dp[i]);
		printf("%lld\n", sum);
		exit(0);
	}
}

namespace task{
	int dp[110], sum3[110], vec[110];
	queue<int> q;
	struct matrix{
		int a[110][110];
		int n, m;
		matrix() {memset(a, 0, sizeof(a));}
		matrix(int x, int y):n(x),m(y) {memset(a, 0, sizeof(a));}
		void resize(int a, int b) {n=a; m=b;}
		void put() {for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) cout<<a[i][j]<<' '; cout<<endl;}}
		inline int* operator [] (int t) {return a[t];}
		inline matrix operator * (matrix& b) {
			matrix ans(n, b.m);
			for (reg i=1; i<=n; ++i)
				for (reg k=1; k<=m; ++k)
					for (reg j=1; j<=b.m; ++j)
						md2(ans[i][j], 1ll*a[i][k]*b[k][j]%mod);
			return ans;
		}
	}mat, tem;
	matrix qpow(matrix &a, ll b) {
		matrix ans=a; --b;
		while (b) {
			if (b&1) ans=ans*a;
			a=a*a; b>>=1;
		}
		return ans;
	}
	void solve() {
		int sum=0, lst=0;
		mat.resize(1, k+1); tem.resize(k+1, k+1);
		int u;
		for (reg i=1; i<=n; ++i) {
			//cout<<"u: "<<u.fir<<' '<<u.sec<<endl;
			lst=dp[a[i]];
			dp[a[i]]=sum;
			md2(dp[a[i]], 1);
			sum=(sum-lst+sum+1)%mod2;
			sum=(sum+mod)%mod2;
			vec[a[i]]=i;
		}
		//for (int i=1; i<=k; ++i) md(sum, dp[i]);
		pair<int, int> s[110];
		for (reg i=1; i<=k; ++i) s[i]=make(vec[i], i);
		sort(s+1, s+k+1);
		for (reg i=1; i<=k; ++i) q.push(s[i].sec);
		int lim=m%k;
		//cout<<"lim: "<<lim<<endl;
		for (reg i=1,mini; i<=lim; ++i) {
			u=q.front(); q.pop();
			lst=dp[u];
			dp[u]=sum;
			md2(dp[u], 1);
			sum=(sum-lst+sum+1)%mod2;
			sum=(sum+mod2)%mod2;
			q.push(u);
		}
		for (reg i=1; i<=k+1; ++i) tem[i][i]=1;
		//cout<<"---tem(ini t)---"<<endl;
		//tem.put(); cout<<endl;
		for (reg i=1; i<=k; ++i) mat[1][i]=dp[i]; mat[1][k+1]=1;
		for (reg i=1; i<=k; ++i) {
			u=q.front(); q.pop();
			memset(sum3, 0, sizeof(sum3));
			for (reg j=1; j<=k; ++j)
				for (reg h=1; h<=k+1; ++h)
					md2(sum3[h], tem[j][h]);
			memcpy(tem[u], sum3, sizeof(sum3));
			++tem[u][k+1];
			q.push(u);
		}
		for (reg i=1; i<=k+1; ++i)
			for (reg j=i+1; j<=k+1; ++j) 
				swap(tem[i][j], tem[j][i]);
		#if 0
		cout<<"---mat---"<<endl;
		mat.put(); cout<<endl;
		cout<<"---tem---"<<endl;
		tem.put(); cout<<endl;
		cout<<"qpow: "<<m/k<<endl;
		#endif
		tem=qpow(tem, m/k);
		mat=mat*tem;
		ll ans=0;
		//cout<<"---ans---"<<endl;
		//for (int i=1; i<=k; ++i) cout<<mat[1][i]<<' '; cout<<endl;
		for (reg i=1; i<=k; ++i) md(ans, mat[1][i]);
		printf("%lld\n", ans);
		exit(0);
	}
}

signed main()
{
	n=read(); m=read(); k=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	if (!m) task3::solve();
	else task::solve();
	//task3::solve();
	//task::solve();
	
	return 0;
}
上一篇:20210824 Prime,Sequence,Omeed


下一篇:唯一主键方案之数据库维护区间分配