2021“MINIEYE杯”中国大学生算法设计超级联赛(5)题解

1003
手玩一下发现如果是一维的只能有两个,二维的只能有三个。
所以得出结论,一维能分开一个。
code:

int T;ll n,k;
int main(){
	scanf("%d",&T);while(T--) scanf("%lld%lld",&n,&k),puts(n<=k+1?"Yes":"No");
}

1004
考虑枚举两个串的开头位置,那么可以通过尺取\(O(n^2)\)得到所有的右端点。
得到右端点后发现是个区间加等差数列的东西。
这个东西直接二阶差分就完事了。
code:

using namespace std;
int T,n,k,len,r,cnt;char S[N+5];ll Q[N+5<<1],Sum[N+5<<1];
I void Solve(){
	re int i,j;scanf("%d%d",&n,&k);scanf("%s",S+1);Me(Q,0);Me(Sum,0);
	for(i=1;i<n;i++){
		for(cnt=r=0,j=1;j<=n-i;j++){
			if(j^1)cnt-=(S[j-1]!=S[j+i-1]);while(r+i<=n&&cnt<=k) r++,cnt+=(S[r]!=S[r+i]);//printf("%d %d %d\n",j,r,i);
			if(r>j+i)Sum[j]++,Sum[j+i]--,Sum[j+i]-=i,Sum[j+i+1]+=i;
			else Sum[j]++,Sum[r]--,Sum[j+i]-=r-j,Sum[j+i+1]+=r-j;
		} 
	}
	for(i=1;i<=n;i++) Sum[i]+=Sum[i-1];for(i=1;i<n;i++) Sum[i]+=Sum[i-1],printf("%lld\n",Sum[i]);
}
int main(){
	freopen("1.in","r",stdin);
	scanf("%d",&T);while(T--) Solve();
}

1006
代码都给你了,直接模拟。
code:

using namespace std;
int T;ll n,m,Ans,tot,pus,ToT1,ToT2,ToT3,ToT4;
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}const ll inv2=mpow(2),inv6=mpow(6);
I void Solve(){
	scanf("%lld",&n);m=n%mod;ToT1=(m+1)*m%mod*inv2%mod;ToT2=m*(m+1)%mod*(2*m+1)%mod*inv6%mod;
	printf("%lld\n%lld\n",((ToT1*ToT2*2+ToT1*ToT1-ToT1-ToT1-ToT2+1-ToT1+1)%mod+mod)%mod,m*ToT1%mod*ToT2%mod*m%mod);
}
int main(){
	freopen("1.in","r",stdin);
	scanf("%d",&T);while(T--) Solve();
}

1007
出题人的英语水平真的可以。
考虑最多就是全放完,最少就是分别在\(x|y|z=1\)的地方放。
注意\(x=y=1\)那一列只留最底下那个就好了。
code:

int T;ll n,m,Ans,tot,pus,ToT1,ToT2,ToT3,ToT4;
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}const ll inv2=mpow(2),inv6=mpow(6);
I void Solve(){
	scanf("%lld",&n);m=n%mod;ToT1=(m+1)*m%mod*inv2%mod;ToT2=m*(m+1)%mod*(2*m+1)%mod*inv6%mod;
	printf("%lld\n%lld\n",((ToT1*ToT2*2+ToT1*ToT1-ToT1-ToT1-ToT2+1-ToT1+1)%mod+mod)%mod,m*ToT1%mod*ToT2%mod*m%mod);
}
int main(){
	freopen("1.in","r",stdin);
	scanf("%d",&T);while(T--) Solve();
}

1008
首先容易想到枚举每个中间的点算贡献。
他下面的节点就是他二进制下一的个数的二次幂。
然后对于每个点我们找到包含它有几个点,有多少个点通过包含它的点上来,除一下就好了。
这个过程可以FWT做。时间复杂度\(O(2^nn)\)
code:

int n,m,k,T,x,G[N+5],inv[N+5],now;ll Ans,F[N+5],W1[N+5],W2[N+5];
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
I void AND(ll *f) {
	for (int o = 2, k = 1; o <= (1<<n); o <<= 1, k <<= 1)
		for (int i = 0; i < (1<<n); i += o)
			for (int j = 0; j < k; j++)
				f[i+j]=(f[i+j]+f[i+j+k])%mod;
}
I void Solve(){
    re int i;Me(F,0);Me(W1,0);Me(W2,0);scanf("%d%d",&n,&m);while(m--) scanf("%d",&x),F[x]++;
	for(i=0;i<(1<<n);i++) W1[i]=1ll*G[i]*F[i],W2[i]=F[i];
	AND(W1);AND(W2);for(Ans=i=0;i<(1<<n);i++) Ans+=W1[i]*mpow(W2[i])%mod;printf("%lld\n",Ans%mod); 
}
int main(){
	freopen("1.in","r",stdin);
	re int i;for(i=0;i<N;i++){now=i;G[i]=1;while(now) G[i]<<=1,now-=now&-now;}scanf("%d",&T);while(T--) Solve();
}

1009
一个小常数\(O(nlogn)\)的,并不知道\(O(n)\)怎么做。
考虑枚举每种权值,然后将这些权值的位置设为1,其它设为-1,那么就是找权值和为正的子区间数。
因为每次只有加一减一,所以可行的起始点和结束点总共最多\(O(n)\)个。
那么直接树状数组维护就好了。
code:

using namespace std;
int n,m,T,r,l,st[N+5],sh,A[N+5],Maxn,B[N+5],C[N+5],Ch,D[N+5],Dh,Fl[N+5],cnt,ToT,L[N+5],R[N+5],F[N+5<<1],Q[N+5];vector<int> G[N+5];ll Ans;
I void insert(int x){while(x<=n*2) F[x]++,x+=x&-x;}
I int find(int x){int ans=0;while(x)ans+=F[x],x-=x&-x;return ans;}
I void clear(int x){while(x<=n*2&&F[x]) F[x]=0,x+=x&-x;;}
I void Solve(){
	Ans=0;re int i,j,h;scanf("%d",&n);for(i=0;i<=Maxn;i++) G[i].clear();Maxn=0;for(i=1;i<=n;i++) scanf("%d",&A[i]),G[A[i]].push_back(i),Maxn=max(Maxn,A[i]);
	for(j=0;j<=Maxn;j++){//printf("%d\n",j);
		m=G[j].size();for(i=0;i<m;i++) B[i+1]=G[j][i],Fl[B[i+1]]=1;
		Ch=Dh=0;for(i=1;i<=m;i++) {
			L[B[i]]=B[i]-1;while(L[B[i]]&&Fl[L[B[i]]])L[B[i]]=max(L[L[B[i]]]-1,0);L[B[i]]&&(C[++Ch]=L[B[i]]);C[++Ch]=B[i];
		}sort(C+1,C+Ch+1);
		for(i=m;i;i--) {
			R[B[i]]=B[i]+1;while(R[B[i]]<=n&&Fl[R[B[i]]]) R[B[i]]=min(R[R[B[i]]]+1,n+1);R[B[i]]<=n&&(D[++Dh]=R[B[i]]);D[++Dh]=B[i];
		}sort(D+1,D+Dh+1);
		for(cnt=ToT=sh=0,l=m-1,r=i=1;i<=Dh;i++){
			while(r<=Ch&&C[r]<=D[i]) insert(2*cnt-C[r]+1+n),st[++sh]=2*cnt-C[r]+1+n,r++;cnt+=Fl[D[i]];Q[D[i]]=2*cnt-D[i];
			Ans+=find(Q[D[i]]+n-1);
		}
		for(i=1;i<=Dh;i++) Q[D[i]]=0;for(i=1;i<=m;i++) L[B[i]]=R[B[i]]=Fl[B[i]]=0;for(i=1;i<=sh;i++) clear(st[i]);
	}printf("%lld\n",Ans);
}
int main(){
	freopen("1.in","r",stdin);
	scanf("%d",&T);while(T--) Solve();
}
上一篇:opensuse免安装mysql


下一篇:鸟哥的linux私房菜勘误表