牛客挑战赛30题解

题面

传送门

orz zsy

A

枚举\(b,c\),那么\(a,d\)的限制就确定了,直接二维数点

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=505;
int sum[N][N],a[N],n;ll res;
int calc(R int x1,R int y1,R int x2,R int y2){return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d",&n);
	fp(i,1,n)scanf("%d",&a[i]),++sum[i][a[i]];
	fp(i,1,n)fp(j,1,n)sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
	fp(i,2,n)fp(j,i+1,n)if(a[i]>a[j])res+=calc(1,1,i-1,a[j]-1)*calc(j+1,a[i]+1,n,n);
	printf("%lld\n",res);
	return 0;
}

B

枚举一下哪一个作为中位数,然后比它大的设为\(+1\),比它小的设为\(-1\),把所有区间和为\(0\)的贡献加上就行了。记得不要忘记中位数是中间两个数平均数的情况

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1e4+5,P=1e9+7;
inline void Add(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int a[N],p[N],pp[N],tl[N<<1],tr[N<<1],n,seed,res,ret;
int main(){
//	freopen("testdata.in","r",stdin);
	n=read(),seed=read();
	fp(i,1,n)a[i]=read();
	p[0]=1;fp(i,1,n)p[i]=mul(p[i-1],seed);
	pp[0]=1;fp(i,1,n)pp[i]=mul(pp[i-1],p[n]);
	fp(i,1,n){
		ret=0;
		for(R int s=0,j=i-1;j;--j)s+=(a[j]>a[i]?1:-1),Add(tl[s+N],pp[j-1]);
		for(R int s=0,j=i+1;j<=n;++j)s+=(a[j]>a[i]?1:-1),Add(tr[s+N],p[j]);
		for(R int x=min(i-1,n-i)+1,j=-x;j<=x;++j)
			ret=(ret+1ll*(tl[j+N]+tl[j+N]+tl[j-1+N]+tl[j+1+N])*tr[-j+N])%P;
		ret=(ret+1ll*(tr[N]+tr[N]+tr[N+1]+tr[N-1])*pp[i-1])%P,
		ret=(ret+1ll*(tl[N]+tl[N]+tl[N+1]+tl[N-1])*p[i])%P,
		ret=(ret+2ll*pp[i-1]*p[i])%P,
		Add(res,mul(ret,a[i]));
		for(R int s=0,j=i-1;j;--j)s+=(a[j]>a[i]?1:-1),tl[s+N]=0;
		for(R int s=0,j=i+1;j<=n;++j)s+=(a[j]>a[i]?1:-1),tr[s+N]=0;
	}
	printf("%d\n",res);
	return 0;
}

C

我们先钦定一个根,表示这个点最后被删,那么其它点删的顺序肯定是从儿子到父亲,直接树形\(dp\),合并儿子的时候用组合数乘一下就好了

然后因为每一个点都可以被钦定为根,那么直接换根\(dp\)就行了

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1e5+5,P=998244353;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
inline int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void Add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int fac[N],ifac[N],sz[N],f[N],n,res;
inline int C(R int n,R int m){return 1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
inline void ins(R int u,R int v){sz[u]+=sz[v],f[u]=1ll*f[u]*f[v]%P*C(sz[u]-1,sz[v])%P;}
inline void del(R int u,R int v){f[u]=1ll*f[u]*ksm(1ll*f[v]*C(sz[u]-1,sz[v])%P,P-2)%P,sz[u]-=sz[v];}
void dfs1(int u,int fa){
	sz[u]=f[u]=1;
	go(u)if(v!=fa)dfs1(v,u),ins(u,v);
}
void dfs2(int u,int fa){
	res=add(res,f[u]);
	go(u)if(v!=fa)del(u,v),ins(v,u),dfs2(v,u),del(v,u),ins(u,v);
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read();
	fac[0]=ifac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],P-2);fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
	for(R int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
	dfs1(1,0),dfs2(1,0);
	printf("%d\n",res);
	return 0;
}

D

省选的时候kcz讲过然而没听懂

很容易写出一个暴力

\[\sum_{i=l}^r {i+n-1\choose n-1}{s-i+m-1\choose m-1} \]

即枚举选了多少个步兵,然后用插板法算出方案数

我们对这个换一种角度考虑,可以看做是从\((0,0)\)走到\((s,n+m)\),且必须经过\((l,n),(r,n)\)这条直线的方案数

这个就等价于第\(l\)步向右走时纵坐标在\((0,n-1)\)的方案数减去第\(r+1\)步向右走时在\((0,n-1)\)的方案数

ps:关于第\(p\)步向右走时在\((0,n-1)\)的方案的计算的话,我们枚举一下就行了,即为

\[\sum_{i=0}^{n-1}{p-1+i\choose i}{s+n+m-p-i\choose n+m-i} \]

其中前面是\(p-1+i\)是因为最后一步强制向右走

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=2e7+5,P=998244353;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int inv[N],f[N],g[N];
int n,m,s,l,r;
int calc(R int p){
	if(p>s)return 0;
	int res=0;
	f[0]=g[0]=1;
	fp(i,1,n+m){
		g[i]=1ll*g[i-1]*(p+i-1)%P*inv[i]%P,
		f[i]=1ll*f[i-1]*(s-p+i)%P*inv[i]%P;
	}
	fp(i,0,n-1)res=add(res,mul(f[n+m-i],g[i]));
	return res;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d%d%d%d",&n,&m,&s,&l,&r);
	inv[0]=inv[1]=1;fp(i,2,N-1)inv[i]=1ll*(P-P/i)*inv[P%i]%P;
	printf("%d\n",dec(calc(l),calc(r+1)));
	return 0;
}

E

首先我们可以很容(jian)易(nan)地发现,题目所求就是所有路径的长度之和

那么我们考虑每条边会在多少条路径中出现,对于一条树边\((u,v)\),出现次数为\(f_u\times f_v\),其中\(f_u\)表示不经过选定树边的方案数

环边比较复杂,不过仔细观察之后可以发现一个性质,就是环上所有边被经过的次数是一样的(因为每一条路径经过这个环的时候都有顺逆时针两种走法,所以每一条经过它的路径都可以覆盖环上所有的边一次)

我们把仙人掌转化成圆方树,那么可以\(dp\)\(g_u\)表示从\(u\)节点出发到\(u\)子树中的点的路径条数。注意如果儿子是一个方点(也就代表当前这个点是一个环的顶点)那么计算儿子的贡献是要乘上\(2\)。那么\(f_u\)就可以通过换根\(dp\)计算了

然后环的话特殊考虑,对于每个环直接大力算出上面每条边的出现次数,再乘上整个环上所有边的边权之和就行了

然后环上的边的出现次数的话……用换根\(dp\)我们可以算出\(f_u\)表示从\(u\)出发不经过环上的边的路径数,对于任意一条经过这个环的路径都有唯一的顺或逆时针方向使其经过某一条边,那么总的出现次数就是

\[\sum_{i=1}^n\sum_{j=i+1}^n f_i\times f_j \]

(假设环上有\(n\)个点)

然后没有然后了

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(G,u) for(int i=G.head[u],v=G.e[i].v;i;i=G.e[i].nx,v=G.e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=2e5+5;
struct Gr{
	struct eg{int v,nx,w;}e[N<<1];int head[N],tot;
	inline void add(R int u,R int v,R int w=0){e[++tot]={v,head[u],w},head[u]=tot;}
}G,T;
int sum[N],dep[N],fa[N],sz[N],vis[N],dis[N];
int n,m,P,cnt,res;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
void tarjan(int u,int fat){
	fa[u]=fat,dep[u]=dep[fat]+1;
	go(G,u)if(v!=fat){
		if(!dep[v])dis[v]=add(dis[u],G.e[i].w),tarjan(v,u);
		else if(dep[v]>dep[u]){
			sum[++cnt]=add(G.e[i].w,dec(dis[v],dis[u]));
			for(R int p=v;p!=u;p=fa[p])vis[p]=1,T.add(cnt,p);
			T.add(u,cnt);
		}
	}
}
inline void ins(R int u,R int v){sz[u]=add(sz[u],(v<=n?1:2)*sz[v]);}
inline void del(R int u,R int v){sz[u]=dec(sz[u],(v<=n?1:2)*sz[v]);}
void dfs1(int u,int fa){
	sz[u]=(u<=n);
	go(T,u)dfs1(v,u),ins(u,v);
}
void dfs2(int u,int fa){
	if(u>n){
		int ret=0,tmp=sz[fa];
		go(T,u)ret=add(ret,mul(tmp,sz[v])),tmp=add(tmp,sz[v]);
		res=add(res,mul(ret,sum[u]));
	}
	go(T,u){
		del(u,v),res=add(res,1ll*sz[u]*sz[v]%P*T.e[i].w%P),ins(v,u);
		dfs2(v,u),del(v,u),ins(u,v);
	}
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=cnt=read(),m=read(),P=read();
	for(R int i=1,u,v,w;i<=m;++i)u=read(),v=read(),w=read(),G.add(u,v,w),G.add(v,u,w);
	tarjan(1,0);
	fp(u,2,n)if(!vis[u])go(G,u)if(v==fa[u]){T.add(fa[u],u,G.e[i].w);break;}
	dfs1(1,0),dfs2(1,0);
	printf("%d\n",res);
	return 0;
}

F

kcz前几天省选讲过然而我还是没听懂

首先需要知道二次剩余是个什么东西

定义,对于\(P\),如果存在\(a^2\equiv x\pmod{P}\),则称\(x\)为模\(P\)的二次剩余

首先有\(a^2\equiv (P-a)^2\pmod{P}\),所以模\(P\)的二次剩余最多只有\({P-1\over 2}\)个(不考虑\(0\)),而且可以证明这\({P-1\over 2}\)个都有(虽然我不会证就是了)

因为有\(a^2\equiv x\pmod{P}\),根据费马小定理,有\(a^{P-1}\equiv 1\pmod{P}\),那么必有\(x^{P-1\over 2}\equiv 1\pmod{P}\)

根据算术基本定理(我也不知道这是个啥),一个\(n\)次多项式最多只有\(n\)个根(允许有重根),那么多项式\(x^{P-1\over 2}-1\equiv 0\pmod{P}\)最多只有\({P-1\over 2}\)个根,分别对应\(P\)\({P-1\over 2}\)个二次剩余

我们设

\[G(x)=\prod_{i是二次剩余}(x-x_i) \]

显然\(G(x)\)也有\({P-1\over 2}\)个根,且每个根都对应\(P\)的一个二次剩余

因为\(Z_p[x]\)是唯一分解整环,而这两个多项式次数相等,且每一个根都相等,所以他们相等

也就是说有

\[x^{P-1\over 2}-1\equiv \prod_{i是二次剩余}(x-x_i)\pmod{P} \]

我们要求的就是\(G^k(x)\)\(f(x)\)\(\gcd\)

(多项式的\(\gcd\)终于来了……)

那么可以转化成等式左边那个东西的\(k\)次(记为\(H(x)\))和\(f(x)\)\(\gcd\)

先把\(H(x)\)\(f(x)\)取个模,这样就能保证\(H(x)\)的最高次项不超过\(n\)

然后直接辗转相除,把里面的取模改成多项式取模就行了

因为懒得写\(O(n\log n)\)的多项式取模所以直接写了个暴力取模……思路大概讲一下的话就是我们要计算\(A(x)\bmod{B(x)}\),首先有\(B(x)\equiv 0\pmod{B(x)}\),那么设\(B\)的最高次项次数为\(m\),就有\(x^m\equiv -{1\over b_m}\sum_{i=0}^{m-1}b_ix^i\pmod{B(x)}\),把\(A(x)\)中所有次数大于\(m\)的项都这样暴力分解就行了

顺便有一个比较迷的问题……就是我暴力乘和暴力模的时候如果取模就会\(gg\)……直接转\(long\ long\)计算就没问题……不是很明白为什么……

还有就是为什么我总感觉多项式\(\gcd\)的复杂度不太对呢……一个\(n\)次多项式\(A(x)\)\(m\)次多项式\(B(x)\),那么\(B(x)\)\(A(x)\bmod{B(x)}\)的次数分别为\(m\)\(m-1\)……这样的话根本不是一个\(\log\)吧……

哪位鸽鸽知道这个的复杂度怎么算么qwq能在下面留个言么qwq

//minamoto
#include<bits/stdc++.h>
#define R register
#define poly vector<int>
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int P=998244353;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
poly Mul(poly A,poly B){
	int n=A.size(),m=B.size();poly C;C.resize(n+m-1);
	fp(i,0,n-1)fp(j,0,m-1)C[i+j]=(C[i+j]+1ll*A[i]*B[j])%P;
	while(!C.empty()&&!C.back())C.pop_back();
	return C;
}
poly Mod(poly A,poly B){
	int n=A.size(),m=B.size();
	fd(i,n-1,m-1)if(A[i]){
		int t=mul(P-A[i],ksm(B[m-1],P-2));
		fp(j,0,m-1)A[i-j]=(A[i-j]+1ll*t*B[m-1-j])%P;
	}
	while(!A.empty()&&!A.back())A.pop_back();
	return A;
}
poly ksm(poly x,int y,poly z){
	poly res;res.push_back(1);
	for(;y;y>>=1,x=Mod(Mul(x,x),z))if(y&1)res=Mod(Mul(res,x),z);
	return res;
}
poly gcd(poly x,poly y){return y.empty()?x:gcd(y,Mod(x,y));}
int n,k;poly f,g;
int main(){
//	freopen("testdata.in","r",stdin);
	n=read(),k=read(),f.resize(n+1),g.resize(2);
	fp(i,0,n)f[i]=read();
	g[0]=0,g[1]=1,g=ksm(g,(P-1)>>1,f);
	if(g.empty())g.push_back(P-1);else g[0]=dec(g[0],1);
	g=ksm(g,k,f),g=gcd(g,f);
	int m=g.size()-1,t=ksm(g[m],P-2);
	printf("%d\n",m);
	fp(i,0,m)printf("%d%c",mul(g[i],t)," \n"[i==m]);
	return 0;
}
posted @ 2019-03-28 20:14  bztMinamoto  阅读(219)  评论(2编辑  收藏  举报
Live2D