「总结」达哥数学专项

(还真是抖机灵)。

9道题,大概都能做出来的有6道。
剩下的3道实在是恶心。

1.连边 http://hzoj.com/contest/235/problem/1
这个的话其实就是简单的(dp)和容斥了。
具体怎么做呢?
我们设(dp[i][j])为带标号选择(i)条边,图中还剩下(j)个奇点的方案数。
那么得到转移方程:

[egin{aligned} dp[i][j]&+=dp[i-1][j+2]inom{j+2}{2}\ dp[i][j]&+=dp[i-1][j]j(n-j)\ dp[i][j]&+=dp[i-1][j-2]inom{n-j+2}{2}\ dp[i][j]&-=dp[i-2][j](i-1)(inom{n}{2}-(i-2))\ end{aligned}]

前三步都很显然。
最后一步的容斥的意思是,从(i)条带标号的边中选出一条连重复了的,然后减去这条边可能存在的方案。
由于(dp[i-2][j])得到的是准确数值,所以容斥的时候不能选择没有重复的(i-2)条边,这样结果才能是正确的。
同时注意我们求了的是带标号的方案数。
而题目里的边是不带标号的。
所以我们要输出的结果是:(frac{dp[K][0]}{K!})

2.Per http://hzoj.com/contest/235/problem/2
以前做过了,现在重新做一次。
可以考虑一个类似数位(dp)的方法来求。
在前(i-1)位相同,第(i)位小于原串的情况下求出方案并累加即可。
([i,n])中的某种数字个数为(c_j)
由于要去重,所以当第(i)位的时候,需要累加的方案为:

[frac{(n-i)!}{prodlimits_{j=1}^{mx}c_j!}(sumlimits_{j=1}^{a_i-1}c_j) ]

其实相当于钦定第(i)为选的数字。
这样就可以求了。
然而这不是最恶心的。
最恶心的是模数不是质数。
考虑分解中国剩余定理合并。
对于一个(p^k),我们在求阶乘的时候从中抽出全部的(p),然后这样剩下的部分互质了,存在逆元,就可以算了。
对于(c_j)的求法,直接树状数组即可。

3.数三角形 http://hzoj.com/contest/235/problem/3
考虑大力容斥。
全集是:(inom{nm}{3})
需要减去三点共线的情况。
那么以原点为一个端点,(n^2)
枚举另外一个端点((x,y)),得到一条线段,每条线段上有(gcd(x,y))个点,那么这条的贡献就是(gcd(x,y)-1)(去除((x,y)))可以平移得到另外((n-x)(m-y))条线段。
容斥出解。

4.JC的小苹果 http://hzoj.com/contest/235/problem/4
这是之前wxh讲过的。
正常点的思路就是我们考虑一个(dp)+高消的解法。
(dp[x][i])设为当前在第(x)个点,剩下的(hp)(i)的方案数。
这样每层之间转移(a_i!=0)的情况,单层转移的话直接高斯消元即可。
这样复杂度是(O(n^3hp))的。过不掉。
考虑如何优化。
我们发现高斯消元所列出的方程用矩阵乘法来表示就是:

[X*A=B$$,也就是转移矩阵来代表每个变量$x$的转移,而$B$来表示转移之后的答案,相当于一个代入的过程。 这样是一个行向量乘一个正方形矩阵得到一个列向量。 我们现在有的是$B$,而期望得到的是$X$。 怎么做? $$X=B*A^{-1}]

求出(A)矩阵的逆即可。
由于我们的矩阵(A)是一直不变的。
变了的只有(B)常数矩阵,而列向量乘矩阵的复杂度是(O(n^2))的,这样就直接优化成了:(O(n^3+n^2hp))了。

5.城市旅行 http://hzoj.com/contest/235/problem/5
容易想到(LCT)维护。
考虑一个(dp)的过程来合并三段区间(l,x,r)
如果当前的状态是这样的:

[a_1,a_2(ch[x][0]),a_3,a_4(x),a_5,a_6(ch[x][1]) ]

怎么合并呢?
我们考虑两个区间的贡献即可。
左侧区间加入(x)及右侧区间得到的贡献是其中每个元素被区间覆盖的次数,可以发现离(x)越近被区间覆盖的次数越多。
而且是呈等差数列的。具体来说怎么处理呢?
我们发现这个贡献是((sz[ch[x][1]]+1)(a_1*1+a_2*2+a_3*3)),而右侧的贡献也相似。
这样我们只需要维护两个变量分别表示当前点的从左向右和从右向左分别和等差数列对位相乘的答案即可。
设为:(ls[x],rs[x])
再设(sz[x])(x)子树大小,(a[x])为点权,(w[x])(x)的子树(a[x])和,(dp[x])为子树维护区间的答案和。
那么维护的方法就是:

[egin{aligned} sz[x]&=sz[ch[x][0]]+sz[ch[x][1]]+1\ w[x]&=w[ch[x][0]]+w[ch[x][1]]+a[x]\ ls[x]&=ls[ch[x][0]]+ls[ch[x][1]]+(sz[ch[x][0]]+1)(w[ch[x][1]]+a[x])\ rs[x]&=rs[ch[x][0]]+rs[ch[x][1]]+(sz[ch[x][1]]+1)(w[ch[x][0]]+a[x])\ dp[x]&=dp[ch[x][0]]+dp[ch[x][1]]+ls[ch[x][0]](sz[ch[x][1]]+1)+rs[ch[x][1]](sz[ch[x][0]]+1)+a[x](sz[ch[x][0]]+1)(sz[ch[x][1]]+1)\ end{aligned}]

还有一个部分是如何修改。
我们假设子树加上了(d),设加法标记为(f[x])
那么维护的方法就是:

[egin{aligned} f[x]&+=d\ a[x]&+=d\ w[x]&+=sz[x]d\ ls[x]&+=frac{(sz[x]+1)sz[x]}{2}d\ rs[x]&+=frac{(sz[x]+1)sz[x]}{2}d\ dp[x]&+=sumlimits_{i=1}^{n}i(n-i+1)d\ &=dsumlimits_{i=1}^{n}in-i^2+i\ &=d(frac{n^2(n+1)}{2}-frac{n(n+1)(2n+1)}{6}+frac{n(n+1)}{2})\ end{aligned}]

还要注意在换儿子的时候必须要把(ls)(rs)一起换掉。
还要注意在(findrt)的时候在最后找到根之后一定要将它(splay)上去不然会被链式卡成狗。
还要注意。。。。。。

6.上帝与集合的正确用法 http://hzoj.com/contest/235/problem/6
最水没有之一。
发现指数模(varphi),而更高的指数模(varphi(varphi)),更高的要模(varphi(varphi(varphi))),这样递归几次之后就会变成(1)了。
这个时候更高的幂指数均为(0)了。
这时候再次回溯即可,因为模数不一定是质数,所以记得用扩展欧拉定理来做,(+varphi)即可。

7.随机二分图 http://hzoj.com/contest/235/problem/7
是神题了。
想到了拆边不过具体怎么拆还是不清楚。
第一种边不用拆留下来就行。
第二组我们考虑给他拆成两条独立的(50%)的边。
这样的话同时出现的概率为(25%),而应当是(50),所以再次建立一条四连通边来链接四个点,概率为(25%)
考虑如果两条边如果均在完美匹配中,那么概率为(50%50%+25%=50%),而如果有某一条边单独出现在完美匹配中的话,概率是(50%)(这条边单独出现的概率),也是对的,而如果均不在的话,那么概率为(1-50%50%-25%=50%),概率也是对的。
第三组和第二组一样,只不过四连通加入的时候的概率应该设置为(-25%)
因为两条边不可能同时出现。
这样就可以简单的(dp)了。
我们设(dp[s])为当前未完美匹配的点集为(s)的概率。
然后由于点集个数太多我们考虑记搜。
(hash)表即可。
我一开始的做法是记录当前(dp)到第几条边。
然后发现(T)了,原因是状态变成了(n*n*3*2^{2n})太多了。
考虑如何优化。
瓶颈在于如何确定枚举顺序使得每条边不被重复枚举的情况下将状态压缩为(2^{2n})个。
我们强制每次选择的边必须要匹配最高位的点,这样就可以减少状态数了。

8.觉醒力量 http://hzoj.com/contest/235/problem/8
是线段树维护置换的裸题了。
首先给出的大模数不是质数而且很大,而且正好可以拆成:(11*13*17*19)
这样的话我们分别维护四个模数直接用中国剩余定理合并就可以了。
考虑如何应对修改。
当然是线段树了。
维护分别大小为(11,13,17,19)的置换,表示数字(i)通过这段区间之后的到的结果。
这样就可以(4*20logn)的询问和修改了。

9.没头脑和不高兴 http://hzoj.com/contest/235/problem/9
最恶心没有之一。
还要打子程序真烦人而且代码真的难写死了。

第一问求期望(只会这一问)
我们发现要求的其实就是在排序过奇数位置之后的逆序对数了。
我们设一个长度为(n)的序列的逆序对个数为:(P_n)
那么得到:

[E(P_n)=sumlimits_{i=1}^{n-1}sumlimits_{j=i+1}^{n}w(i,j) ]

其中(w(i,j))表示(i,j)形成逆序关系的概率。
1.(i,j)均未被选择(w(i,j)=frac{1}{2})
2.(i,j)均被选择(w(i,j)=0)
3.(i)被选择而(j)未被选择。
4.(j)被选择而(i)未被选择。
(3.4)不是很好算。
我们假设选择排序的个数为(m)个。
考虑当前点是排序中的第(x_i)个。
那么比这个点要小的概率就是:

[frac{x_i}{m+1} ]

也就是考虑直接将没排序的那个扔进来一起排序,这样得到的位置比(x_i)小的概率就是(frac{x_i}{m+1}),因为有一个是比(x_i)小的,所以(x_i)左侧必然有(x_i)个数,而这个没排序的数的位置有(x_i)种。
如果要求比某个数大的话,答案就是:

[frac{m+1-x_i}{m+1} ]

这样的话最终第一问的答案就是:

[E(P_n)=frac{1}{2}inom{n-m}{2}+0inom{m}{2}+sumlimits_{i=1}^{m}frac{i(n-m-i+1)}{m+1}+sumlimits_{i=1}^{m}frac{(m+1-i)(i-1)}{m+1} ]

这个东西可以看出来是一个关于(m)的多项式,不过我懒得化简了,就打个分数直接(O(n))求吧。

现在考虑第二问:求方差。
这个是真的不会了。
不过我们考虑打个暴力来求小数据的方差。
猜测这个东西仍然是一个关于(m)的多项式。
然后直接代入高斯消元求解。
这样的话就可以求出通项了。(暴力打不对我真的佛了)。

现在考虑第三问:动态修改求期望。
前两个情况不需要维护,而第三和第四个情况需要维护一下了。
我们设当前选择了排序的个数为(m)个,当前某个位置为(i)的没有被选中的位置前面有(k)个被选中的位置。
这种情况下这个点的贡献是多少(3和4情况下)。

[egin{aligned} ans&=sumlimits_{i=1}^{k}frac{i}{m+1}+sumlimits_{i=1}^{s-k}frac{i}{m+1}\ &=frac{k(k+1)}{2(m+1)}+frac{(s-k+1)(s-k)}{2(m+1)}\ &=frac{k^2+k+s^2-2sk+k^2+s-k}{2(m+1)}\ &=frac{2k^2+s^2-2sk+s}{2(m+1)}\ end{aligned}]

那么我们只需要维护每个位置的(k)(k^2)就可以解决了。
附上std和打表程序。

#include<iostream>
#include<cstdio>
using namespace std;
inline void read(int &x)
{
	x=0;char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48,c=getchar();
}
typedef long long ll;
const int maxn=1e5+5;
int n,m,q;
ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
struct frac{
	ll a,b;
	void clear() {a=b=0;}
	void trc() {if(b<0) b=-b,a=-a;ll d=gcd(a,b);a/=d;b/=d;}
	void print() {trc();printf("%lld/%lld
",a,b);}
	void set(ll x,ll y) {a=x;b=y;trc();}
	frac friend operator + (const frac x,const frac y)
	{
		ll d=gcd(x.b,y.b),t=x.b*y.b/d;
		frac z=(frac){t/x.b*x.a+t/y.b*y.a,t};
		return z;
	}
	frac friend operator - (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.b-x.b*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator * (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator / (const frac x,const frac y) {return x*(frac){y.b,y.a};}
	frac friend operator ^ (const frac x,const int p) {return x*x;}
}ans,res,a[4];
struct SGT{
	struct SegmentTree{
		int l,r,d0,d1,f;ll fr,se;
	}zt[maxn<<2];
	void FoundData(int x)
	{
		zt[x].d0=zt[x<<1].d0+zt[x<<1|1].d0;
		zt[x].d1=zt[x<<1].d1+zt[x<<1|1].d1;
		zt[x].fr=zt[x<<1].fr+zt[x<<1|1].fr+1LL*zt[x<<1].d1*zt[x<<1|1].d0;
		zt[x].se=zt[x<<1].se+zt[x<<1|1].se+1LL*zt[x<<1].d1*zt[x<<1].d1*zt[x<<1|1].d0+2LL*zt[x<<1].d1*zt[x<<1|1].fr;
	}
	void Upit(int x,int f) 
	{
		if(!f) zt[x].d0=zt[x].r-zt[x].l+1,zt[x].d1=0;
		else zt[x].d1=zt[x].r-zt[x].l+1,zt[x].d0=0;
		zt[x].fr=zt[x].se=0;zt[x].f=f;
	}
	void LazyDown(int x)
	{
		if(zt[x].f==-1) return ;
		Upit(x<<1,zt[x].f);Upit(x<<1|1,zt[x].f);
		zt[x].f=-1;
	}
	void BuildTree(int x,int l,int r)
	{
		zt[x].f=-1;zt[x].l=l;zt[x].r=r;
		if(l==r) 
		{
			if(l&1) zt[x].d1=1;
			else zt[x].d0=1;
			return ;
		}
		int mid=l+r>>1;
		BuildTree(x<<1,l,mid);
		BuildTree(x<<1|1,mid+1,r);
		FoundData(x);
	}
	void Updata(int x,int L,int R,int op)
	{
		if(L<=zt[x].l&&zt[x].r<=R) return Upit(x,op),void();
		int mid=zt[x].l+zt[x].r>>1;
		LazyDown(x);
		if(L<=mid) Updata(x<<1,L,R,op);
		if(R>mid) Updata(x<<1|1,L,R,op);
		FoundData(x);
	}
	void Query()
	{
		ll s=zt[1].d0,t=zt[1].d1,k=zt[1].fr,k2=zt[1].se;
		ans.set(1LL*(n-t)*(n-t-1),4);res.set(s*(t*t+t)+2*(k2-t*k),2*(t+1));
		ans=ans+res;ans.print();putchar('
');
	}
}sgt;
int main()
{
	read(n);read(q);m=(n-1)/2+1;
	ans.set(1LL*(n-m)*(n-m-1)/2,2);
	for(int i=1;i<=m;++i) res.a+=1LL*(n-m+1-i)*i,res.a+=1LL*(m+1-i)*(i-1);
	res.b=m+1;res.trc();ans=ans+res;ans.print();ans.clear();res.clear();
	ans.set(0,1);res.set(m-(n&1),1);
	if(n&1) a[1].set(-29,360),a[2].set(11,72),a[3].set(3,20);
	else a[1].set(23,360),a[2].set(13,360),a[3].set(3,20);
	ans=a[3]*res*res*res+a[2]*res*res+a[1]*res;
	ans.print();
	sgt.BuildTree(1,1,n);
	for(int i=1,op,x,y;i<=q;++i)
	{
		read(x),read(y),read(op);
		sgt.Updata(1,x,y,op);
		sgt.Query();
	}
	return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;
const int maxn=10;
int n,ct,a[maxn],tmp[maxn],b[maxn],d[maxn],c[maxn];
ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
struct frac{
	ll a,b;
	void clear() {a=0;b=1;}
	void trc() {ll d=gcd(a,b);a/=d;b/=d;if(b<0) a=-a,b=-b;}
	void print() {printf("%lld/%lld",a,b);}
	void set(ll x,ll y) {a=x;b=y;trc();}
	frac friend operator + (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.b+x.b*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator - (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.b-x.b*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator * (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator / (const frac x,const frac y) {return x*(frac){y.b,y.a};}
	frac friend operator ^ (const frac x,const int p) {return x*x;}
}ans,g,p,res;
int cal()
{
	int res=0;
	for(int i=1;i<=n-1;++i)
		for(int j=i+1;j<=n;++j)
			res+=(b[i]>b[j]);
	return res;
}
main()
{
	freopen("ans.in","w",stdout);
	int fac[12];
	for(int i=fac[0]=1;i<=9;++i) fac[i]=fac[i-1]*i;
	for(n=3;n<=9;n+=2)
	{
		int m=(n-1)/2+1;
		ans.set(1LL*(n-m)*(n-m-1)/2,2);
		for(int i=1;i<=m;++i) res.a+=1LL*(n-m+1-i)*i,res.a+=1LL*(m+1-i)*(i-1);
		res.b=m+1;res.trc();ans=ans+res;g=ans;ans.clear();res.clear();
		for(int i=1;i<=n;++i) a[i]=i;
		do{
			for(int i=1;i<=n;i+=2) tmp[++ct]=a[i];
			for(int i=1;i<=n;++i) b[i]=a[i];
			sort(tmp+1,tmp+ct+1);
			ct=0;
			for(int i=1;i<=n;i+=2) b[i]=tmp[++ct];
			ct=0;
			int tmp=cal();p.set(tmp,1);
			ans=ans+(p-g)*(p-g);
		}while(next_permutation(a+1,a+n+1));
		g.set(1,fac[n]);ans=ans*g;
		ans.print();puts("");
	}
	return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=10;
inline void read(int &x)
{
	x=0;char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48,c=getchar();
}
int n;
ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
ll lcm(ll a,ll b) {return a*b/gcd(a,b);}
struct frac{
	ll a,b;
	void clear() {a=b=0;}
	void trc() {ll d=gcd(a,b);a/=d;b/=d;if(b<0) a=-a,b=-b;}
	void print() {printf("%lld/%lld",a,b);}
	void set(ll x,ll y) {a=x;b=y;trc();}
	frac friend operator + (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.b+x.b*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator - (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.b-x.b*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator * (const frac x,const frac y)
	{
		frac z=(frac){x.a*y.a,x.b*y.b};
		return z.trc(),z;
	}
	frac friend operator / (const frac x,const frac y) {return x*(frac){y.b,y.a};}
	frac friend operator ^ (const frac x,const int p) {return x*x;}
}a[maxn][maxn],b[maxn];
void Gauss()
{
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
			if(i!=j)
			{
				frac r=a[j][i]/a[i][i];
				for(int k=1;k<=n;++k) a[j][k]=a[j][k]-r*a[i][k];b[j]=b[j]-r*b[i];
			}
	}
	puts("[]");
	for(int i=1;i<=n;b[i].print(),++i,puts(""))
		for(int j=1;j<=n;++j)
			a[i][j].print(),putchar(' ');
	puts("[]");
	for(int i=1;i<=n;++i) b[i]=b[i]/a[i][i],b[i].print(),puts("");
}
int main()
{
	freopen("ans.in","r",stdin);n=4;
	for(int i=1,g,p,t,w;i<=n;++i)
	{
		read(g);read(p);
		b[i].set(g,p);
		t=i;
		for(int j=1;j<=n;++j,t=t*i) a[i][j].set(t,1);
	}
	for(int i=1;i<=n;b[i].print(),++i,puts(""))
		for(int j=1;j<=n;++j)
			a[i][j].print(),putchar(' ');
	puts("{}");Gauss();
	return 0;
}
/*
1:
2/9
33/20
311/60
211/18


0:

1/4
53/36
137/30
313/30
*/
原文地址:https://www.cnblogs.com/Lrefrain/p/12119560.html