zoukankan      html  css  js  c++  java
  • [多校联考2021] 模拟赛4

    环形划分

    题目描述

    有一个 (n) 个点的完全图,现在你要从中选取若干个三元环,使得这些三元环两两没有重边,并且使得不在三元环中的边数 (leq n-1),请构造出一种方案。

    (nleq 1000)

    解法

    我们只需要写所有满足 (i+j+k=0mod n) 的三元环 ((i,j,k)) 即可。

    显然对于一对不同的 (i,j),仅有唯一的 (k) 满足上述条件,所以该构造不存在环与环之间的重边。

    不合法的情况仅对于一对 (i,j),求出的 (k) 与其一相等。不妨设 (2i+j=0mod n),则 (i,j) 之间的边就不会被选,但是这样的情况最多有 (n-1) 种,因为 (j=n) 的时候一定没有对应的 (i)

    如果你知道这个方法是怎么想到的,请告诉我

    最小表示

    题目描述

    有一个长度为 (n) 的仅含前 (m) 个小写字符的串 (S),你希望知道这个串本质不同的子串有多少个。

    定义两个子串本质不同当且仅当他们的最小表示法不同,最小表示指将该串最靠前出现的字符都替换为字符集的第一个字符,次靠前出现的字符都替换为字符集的第二个字符,以此类推。

    (nleq 5cdot 10^4,mleq 10)

    解法

    显然是重题:T3字符串

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int M = 100005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return f*x;
    }
    int n,a[M],p[M][26],lg[M];char s[M];
    int x[M],y[M],c[M],sa[M],rk[M],dp[M][26];
    vector<int> v[M];long long ans;
    void init()
    {
    	int m=n;
    	for(int i=1;i<=n;i++) c[x[i]=a[i]]++;
    	for(int i=1;i<=m;i++) c[i]+=c[i-1];
    	for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
    	for(int k=1;k<n;k<<=1)
    	{
    		int num=0;
    		for(int i=n-k+1;i<=n;i++) y[++num]=i;
    		for(int i=1;i<=n;i++)
    			if(sa[i]>k) y[++num]=sa[i]-k;
    		for(int i=1;i<=m;i++) c[i]=0;
    		for(int i=1;i<=n;i++) c[x[i]]++;
    		for(int i=1;i<=m;i++) c[i]+=c[i-1];
    		for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
    		swap(x,y);
    		num=x[sa[1]]=1;
    		for(int i=2;i<=n;i++)
    			x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
    		if(num==n) break;
    		m=num;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		rk[sa[i]]=i;
    		if(i>1) lg[i]=lg[i>>1]+1;
    	}
    	int k=0;
    	for(int i=1;i<=n;i++)
    	{
    		int j=sa[rk[i]-1];
    		if(rk[i]==1) continue;
    		if(k) k--;
    		while(i+k<=n && j+k<=n && a[i+k]==a[j+k]) k++;
    		dp[rk[i]][0]=k;
    	}
    	for(int j=1;(1<<j)<=n;j++)
    		for(int i=1;i+(1<<j)-1<=n;i++)
    			dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
    }
    int lcp(int l,int r)
    {
    	l=rk[l];r=rk[r];
    	if(l>r) swap(l,r);l++;
    	int k=lg[r-l+1];
    	return min(dp[l][k],dp[r-(1<<k)+1][k]);
    }
    int exlcp(int x,int y)//后缀x和后缀y的lcp
    {
    	int l=0,l1=v[x].size()-1,l2=v[y].size()-1;
    	for(int i=0;i<l1 && i<l2;i++)
    	{
    		if(v[x][i]-x!=v[y][i]-y) break;//直接不等
    		l++;
    		int t=min(v[x][i+1]-v[x][i],v[y][i+1]-v[y][i])-1;
    		int tmp=lcp(v[x][i]+1,v[y][i]+1); 
    		l+=min(tmp,t); 
    		if(tmp<t) break;//达不到整段 
    	}
    	return l;
    }
    bool cmp(int x,int y)
    {
    	int l=exlcp(x,y),c1=0,c2=0;
    	if(l==n-x+1 || l==n-y+1)
    		return x>y;//长度小的放前边
    	if(p[x][s[x+l]-'a']==x+l) c1=n+1;
    	else c1=a[x+l];
    	if(p[y][s[y+l]-'a']==y+l) c2=n+1;
    	else c2=a[y+l];
    	//只要能决出胜负就行,因为不可能有两个特殊位置
    	return c1<c2; 
    }
    signed main()
    {
    	freopen("string.in","r",stdin);
    	freopen("string.out","w",stdout);
    	n=read();read();
    	scanf("%s",s+1);
    	//先把原串改写了
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=i-p[0][s[i]-'a'];
    		p[0][s[i]-'a']=i;
    	}
    	init();//后缀数组
    	//找到这个位置后面第一个字符
    	for(int i=n;i>=1;i--)
    	{
    		for(int j=0;j<26;j++)
    			p[i][j]=p[i+1][j];
    		p[i][s[i]-'a']=i;
    		for(int j=0;j<26;j++)
    			if(p[i][j]) v[i].push_back(p[i][j]);
    		sort(v[i].begin(),v[i].end());
    		v[i].push_back(n+1);
    	}
    	for(int i=1;i<=n;i++) x[i]=i;
    	sort(x+1,x+1+n,cmp);
    	ans=1ll*n*(n+1)/2;
    	for(int i=2;i<=n;i++)
    		ans-=exlcp(x[i],x[i-1]);
    	printf("%lld
    ",ans);
    }
    

    置换矩阵

    题目描述

    有一个长为 (n) 的序列 (a) 和一个长度为 (n) 的置换 (p),现在用他们生成出一个矩阵 (A)

    • (A_{1,j}=a_j)
    • (A_{i,j}=A_{i-1,p_j}(igeq 2))

    (A) 的行列式,答案模 (1e9+7)

    (nleq 5000)

    ( t subtask1:) 排列 (p) 的环个数 (>1)

    ( t subtask2:) (p_1=n,p_i=i-1(i>1))

    解法

    不难发现对于 ( t subtask1) 的情况可以特判掉,答案就是 (0)

    证明略过了,因为我也不是很清楚

    对于任意置换 (p) 都可以较为容易地转成 ( t subtask2) 的循环置换,那么我们就只讨论 (p) 为循环置换的情况:

    [A=left(egin{matrix}a_0&a_1&...&a_{n-1}\ a_{n-1}&a_0&...&a_{n-2}\ ...&...&...&...\ a_1&a_2&...&a_0end{matrix} ight) ]

    定义一个辅助矩阵 (C)(w) 表示单位复根):

    [C=left( egin{matrix} 1&1&...&1\ 1&w_n^1&...&w_{n}^{n-1}\ 1&w_n^2&...&w_n^{2(n-1)}\ ..&..&...&...\ 1&w_n^{n-1}&...&w_{n}^{(n-1)^2} end{matrix} ight) ]

    因为 (C) 为范德蒙德矩阵,所以可以这样算行列式:

    [det(C)=prod_{0leq i<j<n}(w^j-w^i) ot=0 ]

    这里给出范德蒙德行列式的证明:

    [D=left(egin{matrix} 1&1&1&...&1\ x_1&x_2&x_3&...&x_n\ ...\ x_1^{n-1}&x_2^{n-1}&x_3^{n-1}&...&x_n^{n-1} end{matrix} ight)\ ]

    从下往上每一行依次建出上一行的 (x_1) 倍,得到:

    [D=left(egin{matrix} 1&1&...&1\ x_1-x_1&x_2-x_1&...&x_n-x_1\ ...\ x_1^{n-1}-x_1^{n-1}&x_2^{n-1}-x_1x_2^{n-2}&...&x_n^{n-1}-x_1x_n^{n-2} end{matrix} ight) ]

    不难发现第一行第一列可以直接去掉了,后面的列都可以提公因式的:

    [D=prod_{1<j}(x_j-x_1)left(egin{matrix} 1&...&1\ ...\ x_2^{n-2}&...&x_n^{n-2} end{matrix} ight) ]

    然后继续递归下去就可以得到最终的结论了:

    [D=prod_{1leq i<jleq n}(x_j-x_i) ]

    (f(x)=sum_{i=0}^{n-1}a_ix^i),我们对 (A,C) 做矩阵乘法(前面成的 (w^{xk}) 表示循环位移 (x) 步):

    [AC=left(egin{matrix} f(1)&f(w)&f(w^2)&...&f(w^{n-1})\ f(1)&wf(w)&w^2f(w^2)&...&w^{n-1}f(w^{n-1})\ ...\ f(1)&w^{n-1}f(w)&w^{2(n-1)}f(w^2)&...&w^{(n-1)^2}f(w^{n-1}) end{matrix} ight) ]

    两个可以转化为行列式的等价关系(原理就是可以消成上三角矩阵之后再乘):

    [det(A)det(C)=Big(prod_{i=0}^{n-1}f(w^i)Big)cdotdet(C) ]

    [det(A)=prod_{i=0}^{n-1}f(w^i) ]

    问题变成了求上市带模的值(如果不带模可以快速傅里叶变换的)

    下面就完全是神仙操作了,如果你知道怎么想到的,请告诉我

    (a_{0sim n-1}) 表示 (n) 次多项式 (A)(n) 个根,(b_{0sim m-1}) 表示 (m) 次多项式 (B)(m) 个根。

    (A(x)=([x^n]A)prod_{i=0}^{n-1}(x-a_i)),这个柿子可以理解成插值((n) 个零点插出来),但注意还要带上最高项的系数。

    定义函数 (F(A,B)=([x^m]B)^nprod_{i=0}^{m-1}A(b_i)),设 (g=x^n-1),不难发现 (F(f,g)) 就是答案,所以我们只需要研究 (F(A,B)) 怎么算即可,下面是他和 (F(B,A)) 的关系(我们展开之后再换位):

    [egin{aligned} F(A,B)&=([x^m]B)^nprod_{i=0}^{m-1}A(b_i)\ &=([x^m]B)^nprod_{i=0}^{m-1}([x^n]A)prod_{j=0}^{n-1}(b_i-a_i)\ &=([x^m]B)^n([x^n]A)^mprod_{i=0}^{m-1}prod_{j=0}^{n-1}(b_i-a_i)\ &=(-1)^{nm}([x^n]A)^mprod_{i=0}^{n-1}B(a_i)\ &=(-1)^{nm}F(B,A) end{aligned} ]

    类似辗转相除还有下面的柿子:

    [egin{aligned} F(A-B,B)&=([x^m]B)^nprod_{i=0}^{m-1}(A(b_i)-B(b_i))\ &=([x^m]B)^nprod_{i=0}^{m-1}A(b_i)\ &=F(A,B) end{aligned} ]

    有了上面这两条之后就可以直接做了,第一个柿子用于交换 (A,B),第二个告诉我们可以直接做多项式取模归约到更小的情况,我们在 (m=0) 的时候跳出即可,(A) 的最高项系数为 (0) 是就更新下 (n) 即可。

    至于代码 (...) 就看 抽水马桶 的吧。

  • 相关阅读:
    php设计模式注册表模式
    zend studio
    java中模仿C++实现条件编译
    javascript去掉字符串空格——转
    领域模型谈实体对象和值对象
    面向对象语言的多分派、单分派、双重分派
    Java中Split函数的用法技巧
    web页面导出到Excel乱码解决
    在web项目中如何使用rdlc报表
    ASP.NET控件10个最有用的属性详解
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14619777.html
Copyright © 2011-2022 走看看