zoukankan      html  css  js  c++  java
  • 「联考day7」模拟题解

    (T1) : 宇宙飞船

    题目大意:

    给定飞船坐标,相邻飞船连绳,交换飞船使绳子不相交,捄最多有多少不用移动

    思路:

    没啥好说的,skyh都没A

    性质很裸,构造合法序列,每一次只能选最大值(值指坐标)或最小值

    因为要是当前选了次大或次小值,那么下次选最大最小值时一定会与当前绳相交

    因此枚举每一个点作为合法序列的最后一个值

    那么这个新构造出的序列就分为一个严格上升和严格下降的序列,且结尾都是当前枚举的最后一个值

    如,(n=6),枚举的最后一个值为(4),那么前面一定有:(1) (2) (3)(6) (5)两个序列,可以不连续

    因为每次必选最大最小值,所以选完最大值,最大值会-1,同理最小值+1

    考虑构造的序列与当前序列的区别,如果有顺序不符严格上升下降的点就需要交换,没有就需要从这个点后面调过来

    即顺序符合上升下降就可以不用留在原地

    所以直接求出以当前点为结尾的最长上升子序列和最长下降子序列,相加-1即为答案

    代码:

    int n,m,a[maxn],ans,f[maxn],low[maxn],tot;
    #define mid ((l+r)>>1)
    int Binary(int l,int r,int key){
    	while(l<=r){
    		if(low[mid]>=key)l=mid+1;
    		else r=mid-1;
    	}
    	return l;
    }
    int main(){
    	freopen("spaceship.in","r",stdin);
    	freopen("spaceship.out","w",stdout);
    	n=read();
    	for(int i=1;i<=n;i++)a[i]=read();
    	low[++tot]=a[1];
    	for(int i=2;i<=n;i++){
    		if(a[i]>=low[tot])low[++tot]=a[i],f[i]=tot;
    		else {
    			int id=lower_bound(low+1,low+1+tot,a[i])-low;
    			low[id]=a[i];f[i]=id;
    		}
    	}
    	memset(low,0,sizeof(low));
    	low[tot=1]=a[1];
    	for(int i=2;i<=n;i++){
    		if(a[i]<=low[tot])low[++tot]=a[i],f[i]+=tot-1;
    		else {
    			int id=Binary(1,tot,a[i]);
    			low[id]=a[i];
    			f[i]+=id-1;
    		}
    		ans=max(ans,f[i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    (T2):路径计数

    题目大意:

    思路:

    暴力做法是暴力建边跑拓扑

    打表发现:

    • 所有字符都相同方案数为:(n)
    • 相邻字符都不同方案数为:(2^n-1)

    墸一分析

    相邻字符都不同,能走到的路径是类二叉树(这里最下层的字符虽然相同,但是路径不同),因为深度是(n),所以方案数为:(2^n-1)

    所有字符都相同,能走到的路径是链状的,所以是(n)

    所以对于所有情况,算重的部分是相同字符串

    用全集减去相同字符串当前的贡献加上相同字符串原本的贡献就是答案

    即:

    [ans=2^n-1-柿子1+柿子2 ]

    柿子1是相同字符串当前的贡献,柿子2是相同字符串原本的贡献

    考虑每一部分

    第一部分:

    先明确一个东西:

    记当前串左边有(x)个字符,右边有(y)个字符,那么由整串(s)走到当前串的方案数为((_x^{x+y}))

    证明:可以想象成走矩阵,每次可以左侧走一或右侧走一

    所以考虑当前串每一个串的贡献,累加得到:

    [sum_{i=0}^{len-1}sum_{j=0}^{len-1-i}inom{x+i+y+j}{x+i} ]

    (i)(j)分别表示左右向内延展的长度

    发现每一项组合数下标都是(x+i)

    应用杨辉三角的性质:

    [sum_{i=0}^{n}inom{x}{i}=inom{x+1}{i+1} ]

    (证明:(f_i^j=f_{i-1}^j+f_{i-1}^{j-1}),然后裂项相消)

    所以化简为(用前缀和相减):

    [sum_{i=0}^{len-1}left (inom{x+y+len}{x+1}-inom{x+y+i}{x+1} ight ) ]

    第二部分:

    当前相同串只有前后缀有贡献,贡献为前后缀长度

    整个相同串的贡献为(len),整串(s)走到相同串的方案数为(inom{x+y}{x}),总贡献为(len imes inom{x+y}{x})

    考虑前后缀:

    前缀(x,x+len-i-1)要从(x-1,x+len-i-1)转移过来,前缀贡献为(len-i),方案数为到达(x-1,x+len-i-1)的方案数为((len-i) imes inom{x+y+i-1}{x-1}),后缀同理

    第二部分总贡献为:

    [sum_{i=1}^{len-1}(len-i) imes left(inom{x+y+i-1}{x-1}+inom{x+y+i-1}{y-1} ight)+len imes inom{x+y}{x} ]

    全柿子为:

    [ans=2^n-1-sum_{i=0}^{len-1}left (inom{x+y+len}{x+1}-inom{x+y+i}{x+1} ight)+sum_{i=1}^{len-1}(len-i) imes left(inom{x+y+i-1}{x-1}+inom{x+y+i-1}{y-1} ight)+len imes inom{x+y}{x} ]

    枚举每一个字符时直接跳到最远的相同串尾,枚举(i),每个字符被遍历两次,时间复杂度(O(2 imes n))

    代码:

    /* cinput
    aabbbbbaaabbbbbbbaaa
    */
    #include<bits/stdc++.h>
    #define re register 
    #define ll long long
    #define gc getchar()
    #define getch while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=gc;}
    #define getnu while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-'0';ch=gc;}
    using namespace std;
    inline ll read(){ll s=0,f=1;char ch=gc;getch;getnu;return s*f;}
    const int p=998244353;
    const int N=5e5+10;
    int ine[N],f[N],fac[N];
    char s[N];
    void pre(){
    	f[0]=fac[0]=ine[1]=1;
    	for(re int i=1;i<=N-5;i++)fac[i]=1ll*fac[i-1]*i%p;
    	for(re int i=2;i<=N-5;i++)ine[i]=1ll*(p-p/i)*ine[p%i]%p;
    	for(re int i=1;i<=N-5;i++)f[i]=1ll*f[i-1]*ine[i]%p;
    }
    ll qpow(ll a,ll b,ll res=1){
    	while(b){
    		if(b&1)res=res*a%p;
    		a=a*a%p;b>>=1;
    	}
    	return res;
    }
    ll C(ll n,ll m){
    	if(!m||n==m)return 1;
    	return 1ll*fac[n]*f[m]%p*f[n-m]%p;
    }
    int main(){
    	freopen("counting.in","r",stdin);
    	freopen("counting.out","w",stdout);
    	pre();
    	scanf("%s",s+1);
    	int n=strlen(s+1);
    	ll ans=qpow(2,n)-1;
    	for(int i=1;i<=n;){
    		int id=0;
    		for(int j=i+1;j<=n;j++)if(s[j]!=s[i]){id=j-1;break;}
    		if(!id)id=n;
    		int len=id-i+1;
    		if(len==1)continue;
    		int x=i-1,y=n-id;
    		ll sum=0;
    		for(int j=0;j<=len-1;j++)sum=((sum+C(x+y+len,x+j+1))%p-C(x+y+j,x+j+1)+p)%p;
    		ans=((ans-sum)%p+p)%p;
    		ans=(ans+len*C(x+y,x))%p;
    		for(int j=1;j<=len-1;j++)ans=(ans+1ll*(len-j)*((C(x+y+j-1,x-1)+C(x+y+j-1,y-1))%p)%p)%p;
    		i=id+1;
    	}
    	printf("%lld
    ",ans);
    }
    
    

    (T3):树和森林

    题目大意:

    梦里啥都有

    思路:

    子任务二比子任务一好想(然而考场只想那拿子任务一的)

    子任务二:

    优先考虑每个叶子节点,叶子节点度数不符直接删去连接叶子节点的边,非叶子节点也可以这样处理

    因此方案数唯一,不用考虑其他构造

    根节点特判一下有无解

    代码:

    void DFS5(int u,int fa,int rt){
    	vis[u]=1;
    	for(int x=head[u];x;x=e[x].next){
    		int v=e[x].to;
    		if(v==fa||black[x])continue;
    		DFS5(v,u,x);
    		if(a[v]!=rd[v]%2)rd[v]--,rd[u]--,black[x]=1;
    	}
    }
    void solve2(){
    	memset(g,0,sizeof(g));memset(vis,0,sizeof(vis));ans=0;
    	for(int i=1;i<=n;i++){
    		if(!vis[i]){
    			DFS5(i,0,0);
    			if(rd[i]%2!=a[i])return puts("-1"),void();
    		}
    	}
    	for(int i=1;i<=tot;i+=2){
    		if(black[i]||black[i+1])continue;
    		g[++ans]=(i+1)/2;
    	}
    	printf("%lld
    ",ans);
    	for(int i=1;i<=ans;i++)printf("%lld ",g[i]);puts("");
    }
    

    子任务一:

    分两颗树和三颗树考虑

    两颗树:

    柿子很好推:

    [ans=frac{sum1}{2}+frac{sum2}{2}+size1 imes size2+maxw1 imes size2 +maxw2 imes size1 ]

    (f_u)表示(u)到当前树每个节点的距离和,(sum1=sum_{uin第一颗树}f_u)(sum2=sum_{uin第二颗树}f_u)(maxw1=max(f_u)(uin第一颗树)),(maxw2=max(f_u)(uin第二颗树))
    (frac{sum1}{2}+frac{sum2}{2})是两颗树内部的贡献
    (size1 imes size2)是新建的边走过的次数
    (maxw1 imes size2)表示第一颗树汇集到点(u)的最大贡献,汇集的意思是,树内其它点到达其他树必须经过连接点,每个点要走(size2)次,单次汇集的贡献为(maxw1)
    (maxw2 imes size1)同理
    (f_u)可以通过换根DP球出,复杂度(O(2 imes n))

    三颗树:

    三颗树上难度了

    钦定第二颗树连接左右两颗树,连接点分别为(u),(y)

    柿子为:

    [ans=frac{sum1}{2}+frac{sum2}{2}+frac{sum3}{2}+(maxw1+size1) imes (n-size1)+(maxw3+size3) imes(n-size3)+f_u imes size1+f_y imes size3+dis(u,y) imes size1 imes size3 ]

    前面大致跟两颗树一样

    (f_u imes size1+f_y imes size3)表示第二颗树上的点走到第一颗树和第三颗树的贡献
    (dis(u,y) imes size1 imes size3)表示第一颗树走到第三颗树经过两个点之间的贡献

    拆开柿子:

    [f_u imes size1+f_y imes size3+dis(u,y) imes size1 imes size3= ]

    [f_u imes size1+f_y imes size3+(depth_u+depth_y-depth_lca) imes size1 imes size3= ]

    [f_u imes size1+depth_u imes size1 imes size3+f_y imes size3+depth_y imes size1 imes size3 + depth_lca imes size1 imes size3 ]

    分别维护即可

    代码:

    
    
    /* cinput
    8 5
    BBWWWBBW
    1 2
    2 3
    4 5
    6 7
    7 8
    */
    #include<map>
    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define int long long
    #define ll long long 
    using namespace std;
    const int maxn=1e6+5,INF=0x3f3f3f3f;
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    int n,m,a[maxn],head[maxn],rt1,rt2,rt3,tot,vis[maxn],belong[maxn],siz[maxn],depth[maxn],black[maxn],cnt1,cnt2,cnt3,maxid,rd[maxn];
    struct Edge{
    	int next,from,to;
    }e[maxn*2];
    ll ans,maxw1,maxw2,maxw3,f[maxn],sum,g[maxn],g1[maxn],max1,max2,max3,sum1,sum2,sum3,maxnow;
    void Add(int x,int y){e[++tot]=(Edge){head[x],x,y};head[x]=tot;}
    void DFS1(int u,int fa,int rt){
    	belong[u]=rt;siz[u]=1;vis[u]=1;
    	if(rt==rt1)cnt1++;
    	if(rt==rt2)cnt2++;
    	if(rt==rt3)cnt3++;
    	for(int x=head[u];x;x=e[x].next){
    		int v=e[x].to;
    		if(v==fa)continue;
    		DFS1(v,u,rt);
    		siz[u]+=siz[v];
    		f[u]+=f[v]+siz[v];
    	}
    }
    void DFS2(int u,int fa,int rt){
    	sum+=f[u];
    	for(int x=head[u];x;x=e[x].next){
    		int v=e[x].to;
    		if(v==fa)continue;
    		depth[v]=depth[u]+1;
    		if(rt==rt1)f[v]=f[u]+1LL*cnt1-2LL*siz[v];
    		if(rt==rt2)f[v]=f[u]+1LL*cnt2-2LL*siz[v];
    		if(rt==rt3)f[v]=f[u]+1LL*cnt3-2LL*siz[v];
    		DFS2(v,u,rt);
    	}
    	if(rt==rt1)maxw1=max(maxw1,f[u]);
    	if(rt==rt2)maxw2=max(maxw2,f[u]);
    	if(rt==rt3)maxw3=max(maxw3,f[u]);
    }
    void DFS3(int u,int fa,int rt,int rtt,int dep){
    	if(rtt==rt1)max1=max(max1,g[u]);
    	if(rtt==rt2)max2=max(max2,g[u]);
    	if(rtt==rt3)max3=max(max3,g[u]);
    	for(int x=head[u];x;x=e[x].next){
    		int v=e[x].to;
    		if(v==fa)continue;
    		if(v!=rt){
    			if(rtt==rt2)g[v]=f[rt]*siz[rt1]+f[v]*siz[rt3]+siz[rt1]*siz[rt3]*dep;
    			if(rtt==rt3)g[v]=f[rt]*siz[rt1]+f[v]*siz[rt2]+siz[rt1]*siz[rt2]*dep;
    			if(rtt==rt1)g[v]=f[rt]*siz[rt2]+f[v]*siz[rt3]+siz[rt2]*siz[rt3]*dep;
    		}
    		DFS3(v,u,rt,rtt,dep+1);
    	}
    }
    #define siz1 siz[rt1]
    #define siz2 siz[rt2]
    #define siz3 siz[rt3]
    void DFS4(int u,int fa,int rtt){
    	ll last1=0,last2=0;vis[u]=1;
    	if(rtt==rt2)last1=siz1*f[u]+depth[u]*siz1*siz3,last2=siz3*f[u]+depth[u]*siz1*siz3,max2=max(max2,last1+last2-2*depth[u]*siz1*siz3);
    	if(rtt==rt3)last1=siz1*f[u]+depth[u]*siz1*siz2,last2=siz2*f[u]+depth[u]*siz1*siz2,max3=max(max3,last1+last2-2*depth[u]*siz1*siz2);
    	if(rtt==rt1)last1=siz3*f[u]+depth[u]*siz2*siz3,last2=siz2*f[u]+depth[u]*siz2*siz3,max1=max(max1,last1+last2-2*depth[u]*siz2*siz3);
    	for(int x=head[u];x;x=e[x].next){
    		int v=e[x].to;
    		if(v==fa)continue;
    		DFS4(v,u,rtt);
    		if(rtt==rt2)max2=max(max2,max(last1+g1[v],last2+g[v])-2LL*depth[u]*siz1*siz3);
    		if(rtt==rt3)max3=max(max3,max(last1+g1[v],last2+g[v])-2LL*depth[u]*siz1*siz2);
    		if(rtt==rt1)max1=max(max1,max(last1+g1[v],last2+g[v])-2LL*depth[u]*siz2*siz3);
    		last1=max(last1,g[v]);
    		last2=max(last2,g1[v]);
    	}
    	g[u]=last1;g1[u]=last2;
    }
    #undef siz1
    #undef siz2
    #undef siz3
    void solve1(){
    	if(!rt3){
    		ans=sum/2+1LL*siz[rt1]*siz[rt2]+1LL*maxw1*siz[rt2]+1LL*maxw2*siz[rt1];
    		printf("%lld
    ",ans);
    	}else{
    		memset(vis,0,sizeof(vis));
    		for(int i=1;i<=n;i++){
    			if(!vis[i])DFS4(i,0,belong[i]);
    		}
    		ll ans1,ans2,ans3;
    		ans2=sum/2+(maxw1+siz[rt1])*(n-siz[rt1])+(maxw3+siz[rt3])*(n-siz[rt3])+max2;
    		ans1=sum/2+(maxw2+siz[rt2])*(n-siz[rt2])+(maxw3+siz[rt3])*(n-siz[rt3])+max1;
    		ans3=sum/2+(maxw1+siz[rt1])*(n-siz[rt1])+(maxw2+siz[rt2])*(n-siz[rt2])+max3;
    		ans=max(ans1,max(ans2,ans3));
    		printf("%lld
    ",ans);
    	}
    }
    void DFS5(int u,int fa,int rt){
    	vis[u]=1;
    	for(int x=head[u];x;x=e[x].next){
    		int v=e[x].to;
    		if(v==fa||black[x])continue;
    		DFS5(v,u,x);
    		if(a[v]!=rd[v]%2)rd[v]--,rd[u]--,black[x]=1;
    	}
    	//if(a[u]!=rd[u]%2)rd[u]--,rd[fa]--,black[rt]=1;
    }
    void solve2(){
    	memset(g,0,sizeof(g));memset(vis,0,sizeof(vis));ans=0;
    	for(int i=1;i<=n;i++){
    		if(!vis[i]){
    			DFS5(i,0,0);
    			if(rd[i]%2!=a[i])return puts("-1"),void();
    		}
    	}
    	for(int i=1;i<=tot;i+=2){
    		if(black[i]||black[i+1])continue;
    		g[++ans]=(i+1)/2;
    	}
    	printf("%lld
    ",ans);
    	for(int i=1;i<=ans;i++)printf("%lld ",g[i]);puts("");
    }
    signed main(){
    	freopen("lct.in","r",stdin);
    	freopen("lct.out","w",stdout);
    	n=read();m=read();
    	char ch;
    	for(int i=1;i<=n;i++){
    		cin>>ch;
    		if(ch=='B')a[i]=1;
    		else a[i]=0;
    	}
    	for(int i=1,x,y;i<=m;i++){
    		x=read(),y=read();Add(x,y);Add(y,x);rd[x]++;rd[y]++;
    	}
    	rt1=rt2=rt3=0;
    	for(int i=1;i<=n;i++){
    		if(!vis[i]){
    			if(!rt1)rt1=i;
    			else if(!rt2)rt2=i;
    			else if(!rt3)rt3=i;
    			DFS1(i,0,i);DFS2(i,0,i);
    		}
    	}
    	solve1();
    	solve2();
    	return 0;
    }
    
    
  • 相关阅读:
    全局变量-静态变量
    System.Web.Optimization 找不到引用,教你如何解决?
    CSS text-decoration 属性
    HTML 5 <span> 标签
    [VS]
    C# 条件表达式max=(a>b)?a:b;含义
    vs 2017 Integrated Security 为sspi 含义
    Visual studio 利用Nuget 控制台安装已经下载好的插件
    使用INTERSECT运算符
    Oracle DB 使用子查询来解决查询
  • 原文地址:https://www.cnblogs.com/614685877--aakennes/p/13905758.html
Copyright © 2011-2022 走看看