zoukankan      html  css  js  c++  java
  • 牛客挑战赛30 简要题解

    牛客挑战赛30题解

    比赛地址

    Orz Anson&Deadecho

    A

    枚举(b,c),这样(a,d)的限制也就确定了,二维数点即可。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N=505;
    int n,a[N],sum[N][N];long long ans;
    int getsum(int x1,int x2,int y1,int y2){
    	return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
    }
    int main(){
    	n=gi();
    	for(int i=1;i<=n;++i)a[i]=gi(),++sum[i][a[i]];
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)
    			sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
    	for(int i=2;i<=n;++i)
    		for(int j=i+1;j<=n;++j)
    			if(a[i]>a[j])
    				ans+=getsum(1,i-1,1,a[j]-1)*getsum(j+1,n,a[i]+1,n);
    	printf("%lld
    ",ans);return 0;
    }
    

    B

    好像被我强行水过去了?

    考虑一个区间的贡献(seed^{(l-1)n+r}),可以拆成(seed^{(l-1)n} imes seed^r),所以依然可以考虑枚举每一个数作为中位数,将大于它的视作(1)小于它的视作(-1),找到所有包含这个数且和为(0)(pm 1)的区间(因为要考虑中位数有两个的情况),将左端点与右端点的权值相乘即可得到答案。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define ll long long
    const int N=10005;
    const int mod=1e9+7;
    int n,seed,a[N],p[N],P[N],tl[N<<1],tr[N<<1],ans;
    inline void add(int &x,int y){x+=y;x>=mod?x-=mod:x;}
    int main(){
    	n=gi();seed=gi();
    	for(int i=1;i<=n;++i)a[i]=gi();
    	p[0]=1;p[1]=seed;
    	for(int i=2;i<=n;++i)p[i]=1ll*p[i-1]*p[1]%mod;
    	P[0]=1;P[1]=p[n];
    	for(int i=2;i<=n;++i)P[i]=1ll*P[i-1]*P[1]%mod;
    	for(int i=1;i<=n;++i){
    		memset(tl,0,sizeof(tl));
    		memset(tr,0,sizeof(tr));
    		int res=0;
    		for(int j=i-1,s=0;j>=1;--j)s+=(a[j]>a[i]?1:-1),add(tl[s+N],P[j-1]);
    		for(int j=i+1,s=0;j<=n;++j)s+=(a[j]>a[i]?1:-1),add(tr[s+N],p[j]);
    		for(int x=min(i-1,n-i)+1,j=-x;j<=x;++j)
    			res=(res+((ll)tl[j+N]+tl[j+N]+tl[j-1+N]+tl[j+1+N])*tr[-j+N])%mod;
    		res=(res+((ll)tr[N]+tr[N]+tr[1+N]+tr[-1+N])*P[i-1])%mod;
    		res=(res+((ll)tl[N]+tl[N]+tl[1+N]+tl[-1+N])*p[i])%mod;
    		res=(res+(ll)P[i-1]*p[i]*2)%mod;
    		ans=(ans+(ll)res*a[i])%mod;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    C

    考虑强制一个点最后一个删,将其作为根节点,这样删点的顺序就一定是从叶子到根,因而可以做一个简单(dp)求出方案数,合并子树时方案数乘上组合数即可。

    现在要求以每个点为根(每个点最后一个删)的答案。直接换根(dp)即可。

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N=1e5+5;
    const int mod=998244353;
    int n,inv[N],jc[N],jcn[N],sz[N],dp[N],ans;vector<int>E[N];
    int fastpow(int a,int b){
    	int res=1;
    	while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    	return res;
    }
    int C(int n,int m){return 1ll*jc[n]*jcn[m]%mod*jcn[n-m]%mod;}
    void work(int u,int v){
    	sz[u]+=sz[v],dp[u]=1ll*dp[u]*dp[v]%mod*C(sz[u]-1,sz[v])%mod;
    }
    void rework(int u,int v){
    	dp[u]=1ll*dp[u]*fastpow(1ll*dp[v]*C(sz[u]-1,sz[v])%mod,mod-2)%mod,sz[u]-=sz[v];
    }
    void dfs1(int u,int f){
    	sz[u]=dp[u]=1;
    	for(int v:E[u])if(v!=f)dfs1(v,u),work(u,v);
    }
    void dfs2(int u,int f){
    	ans=(ans+dp[u])%mod;
    	for(int v:E[u])if(v!=f)rework(u,v),work(v,u),dfs2(v,u),rework(v,u),work(u,v);
    }
    int main(){
    	n=gi();inv[1]=jc[0]=jcn[0]=1;
    	for(int i=2;i<=n;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    	for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=1ll*jcn[i-1]*inv[i]%mod;
    	for(int i=1;i<n;++i){
    		int u=gi(),v=gi();
    		E[u].push_back(v);E[v].push_back(u);
    	}
    	dfs1(1,0);dfs2(1,0);printf("%d
    ",ans);return 0;
    }
    

    D

    不难发现答案式为(sum_{i=l}^rinom{i+n-1}{n-1}inom{s-i+m}{m})

    这是一个(n+m)次多项式然而没办法在(O(n+m))的时间里算出前(n+m)项。

    考虑其组合意义。相当于从((0,0))点出发走到((s,n+m)),每步可以向上走或是向右走,且保证第(n)步向上走时横坐标在([l,r])内的方案数。

    这个等价于求第(l)步向右走时纵坐标在([0,n-1])的方案数减去第(r+1)步向右走时纵坐标在([0,n-1])的方案数,而这些是可以做到(O(n+m))计算的。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N=2e7+5;
    const int mod=998244353;
    int n,m,s,l,r,inv[N],f[N],g[N];
    int cal(int p){
    	if(p>s)return 0;int res=0;
    	//sum_{i=0}^{n-1}inom{i+p-1}{i}inom{n+m-1+s-p}{n+m-i}
    	for(int i=f[0]=g[0]=1;i<=n+m;++i){
    		f[i]=1ll*f[i-1]*(s-p+i)%mod*inv[i]%mod;
    		g[i]=1ll*g[i-1]*(p-1+i)%mod*inv[i]%mod;
    	}
    	for(int i=0;i<n;++i)res=(res+1ll*f[n+m-i]*g[i])%mod;
    	return res;
    }
    int main(){
    	n=gi(),m=gi(),s=gi(),l=gi(),r=gi();inv[1]=1;
    	for(int i=2;i<N;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    	printf("%d
    ",(cal(l)-cal(r+1)+mod)%mod);return 0;
    }
    

    E

    不难发现题目要求的就是仙人掌上每一条路径的长度之和。

    考虑分别计算每一条边出现在了多少条路径中。对仙人掌建圆方树,然后这个方案数就可以用简单换根(dp)求出。注意每经过一个方点(一个环)时方案数要乘(2)

    对于环边,显然环上的每一条边的计算次数都是相同的,于是在(dfs)到方点的同时计算一下答案即可。

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define pi pair<int,int>
    #define mk make_pair
    #define fi first
    #define se second
    const int N=2e5+5;
    int n,tot,m,mod,fa[N],dep[N],dis[N],sum[N],mrk[N],sz[N],ans;
    vector<pi>G1[N],G2[N];
    void link(int u,int v,int w,vector<pi>*G){
    	G[u].push_back(mk(v,w));G[v].push_back(mk(u,w));
    }
    void dfs(int u,int f){
    	fa[u]=f;dep[u]=dep[f]+1;
    	for(pi x:G1[u])
    		if(x.fi!=f)
    			if(!dep[x.fi])dis[x.fi]=(dis[u]+x.se)%mod,dfs(x.fi,u);
    			else if(dep[x.fi]>dep[u]){
    				sum[++tot]=(x.se+dis[x.first]-dis[u]+mod)%mod;
    				for(int v=x.fi;v!=u;v=fa[v])link(tot,v,0,G2),mrk[v]=1;
    				link(tot,u,0,G2);
    			}
    }
    void work(int u,int v){
    	sz[u]=(sz[u]+(v<=n?1:2)*sz[v])%mod;
    }
    void undo(int u,int v){
    	sz[u]=(sz[u]-(v<=n?1:2)*sz[v]+mod+mod)%mod;
    }
    void dfs1(int u,int f){
    	sz[u]=(u<=n);
    	for(pi x:G2[u])if(x.fi!=f)dfs1(x.fi,u),work(u,x.fi);
    }
    void dfs2(int u,int f){
    	if(u>n){
    		int res=0,tmp=0;
    		for(pi x:G2[u])res=(res+1ll*tmp*sz[x.fi])%mod,tmp=(tmp+sz[x.fi])%mod;
    		ans=(ans+1ll*res*sum[u])%mod;
    	}
    	for(pi x:G2[u])
    		if(x.fi!=f){
    			undo(u,x.fi);ans=(ans+1ll*sz[u]*sz[x.fi]%mod*x.se)%mod;
    			work(x.fi,u);dfs2(x.fi,u);undo(x.fi,u);work(u,x.fi);
    		}
    }
    int main(){
    	n=tot=gi();m=gi();mod=gi();
    	for(int i=1,x,y,z;i<=m;++i)x=gi(),y=gi(),z=gi(),link(x,y,z,G1);
    	dfs(1,0);
    	for(int i=2;i<=n;++i)if(!mrk[i])for(pi x:G1[i])if(x.fi==fa[i])link(i,fa[i],x.se,G2);
    	dfs1(1,0);dfs2(1,0);printf("%d
    ",ans);return 0;
    }
    

    F

    考虑一下存在二次剩余的数(x)满足一些什么样的性质。

    (x^{frac{p-1}{2}}equiv1mod p)

    (mod p)意义下恰有(frac{p-1}{2})个数存在二次剩余,即恰有(frac{p-1}{2})个数满足上式。

    根据某些代数高论我们可以得到

    (x^{frac{p-1}{2}}-1=prod_{imbox{存在二次剩余}}(x-i))

    而我们要求的东西即为等式右边的(k)次方与(f(x))(gcd)

    可以先求出((x^{frac{p-1}{2}}-1)^kmod f(x))后再与(f(x))(gcd)。复杂度(O(n^2log p))

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define vi vector<int>
    const int mod=998244353;
    int inv(int a){
    	int res=1,b=mod-2;
    	while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    	return res;
    }
    void print(vi a){
    	int n=a.size();printf("%d
    ",n-1);
    	for(int i=0;i<n;++i)printf("%d ",a[i]);puts("");
    }
    vi mul(vi a,vi b){
    	int n=a.size(),m=b.size();vi c;c.resize(n+m-1);
    	for(int i=0;i<n;++i)
    		for(int j=0;j<m;++j)
    			c[i+j]=(c[i+j]+1ll*a[i]*b[j])%mod;
    	while(c.size()&&!c.back())c.pop_back();return c;
    }
    vi Mod(vi a,vi b){
    	int n=a.size(),m=b.size();
    	for(int i=n-1;i>=m-1;--i)
    		if(a[i]){
    			int t=1ll*(mod-a[i])*inv(b[m-1])%mod;
    			for(int j=0;j<m;++j)a[i-j]=(a[i-j]+1ll*b[m-1-j]*t)%mod;
    		}
    	while(a.size()&&!a.back())a.pop_back();return a;
    }
    vi fastpow(vi a,int b,vi c){
    	vi res;res.push_back(1);
    	while(b){
    		if(b&1)res=Mod(mul(res,a),c);
    		a=Mod(mul(a,a),c);b>>=1;
    	}
    	return res;
    }
    vi gcd(vi a,vi b){
    	if(!b.size())return a;
    	return gcd(b,Mod(a,b));
    }
    int main(){
    	int n=gi(),k=gi();vi f,g;
    	for(int i=0;i<=n;++i)f.push_back(gi());
    	g.push_back(0);g.push_back(1);
    	g=fastpow(g,mod-1>>1,f);
    	if(!g.size())g.push_back(mod-1);else g[0]=(g[0]+mod-1)%mod;
    	g=fastpow(g,k,f);g=gcd(f,g);
    	int t=inv(g[g.size()-1]);
    	for(int i=0;i<g.size();++i)g[i]=1ll*g[i]*t%mod;
    	print(g);return 0;
    }
    
  • 相关阅读:
    关于Xcode的一些方法-15-05-01
    iOS 多线程(NSThread、GCD、NSOperation)
    iOS中View的创建过程
    iOS启动原理及应用生命周期
    UITableView详解
    iOS 字典转模型
    strong和weak
    零碎知识点总结(不定时更新)
    iOS常用第三方类库 Xcode插件
    cocoapods 类库管理利器
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/10513912.html
Copyright © 2011-2022 走看看