牛客练习赛51-A(二分) B(二分) C(数论结论) D(二分匹配) E(思维+二分) F(矩阵DP+树剖)

Contest:https://ac.nowcoder.com/acm/contest/1083#question

当时comet和这个重了,就去打comet了(谁让comet奖品看起来更好一点呢),结果被吊打,最后一个小时才来写这场,想着混个签到抽抽短袖,结果发现题目意外的简单点?(结果成了二分场?是不是数据很水啊....)

A-abc(二分)

题目链接:https://ac.nowcoder.com/acm/contest/1083/A

题目大意:给出一个只含小写字母的字符串S,找出S的子序列abc的个数。

思路:遍历a,二分每个a后面的第一个b,再找出c,随便过。。。

ACCode:

const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

char S[MAXN];
int A[MAXN],B[MAXN],C[MAXN];
int tota,totb,totc;

int main(){
	scanf("%s",S+1);
	int len=strlen(S+1);
	tota=totb=totc=0;
	for(int i=1;i<=len;++i){
		if(S[i]=='a') A[++tota]=i;
		else if(S[i]=='b') B[++totb]=i;
		else if(S[i]=='c') C[++totc]=i;
	}
	ll ans=0;
	for(int i=1;i<=tota;++i){
		int l=1,r=totb,mid;
		while(l<=r){//找到第一个B[mid]>=A[i] 
			mid=(l+r)>>1;
			if(B[mid]<=A[i]) l=mid+1;
			else r=mid-1;
		}
		int posb=l;
		if(posb>totb) continue;
		for(int j=posb;j<=totb;++j){
			l=1,r=totc;
			while(l<=r){
				mid=(l+r)>>1;
				if(C[mid]<=B[j]) l=mid+1;
				else r=mid-1;
			}
			int posc=l;
			if(posc>totc) break;
			ans+=totc-posc+1;
		}
	}printf("%lld\n",ans);
}

B-字串查询(贪心+二分)

题目链接:https://ac.nowcoder.com/acm/contest/1083/B

题目大意:给出一个字符串S,对于q次询问,每次给出字符串p,判断p是不是S的子序列

思路:对于p[i],肯定是找第一个出现在S中的字符,所以二分就好了,随便过。。

ACCode:

const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

char S[MAXN],P[MAXN];
int Pos[30][MAXN],Totx[30],Idx[30];
int n,q;

int main(){
	scanf("%d%d",&n,&q);
	scanf("%s",S+1);
	for(int i=1;i<=n;++i){
		int ch=S[i]-'a'+1;
		Pos[ch][++Totx[ch]]=i;
	}
	while(q--){
		scanf("%s",P+1);int len=strlen(P+1);
		for(int i=0;i<30;++i) Idx[i]=0;
		int flag=1,l,r,mid,pre=Pos[P[1]-'a'+1][1];
		for(int i=2;i<=len;++i){
			int ch=P[i]-'a'+1;
			l=1,r=Totx[ch];
			while(l<=r){
				mid=(l+r)>>1;
				if(Pos[ch][mid]<=pre) l=mid+1;
				else r=mid-1;
			}
			if(l>Totx[ch]){
				flag=0;break;
			}pre=Pos[ch][l];
		}
		if(flag) printf("YES\n");
		else printf("NO\n");
	}
}

C-勾股定理(数论结论)

题目链接:https://ac.nowcoder.com/acm/contest/1083/C

题目大意:给出一个长度l,判断该长度是否是直角三角形的一边,边都只能是整数。输出另外两边。

思路:之前碰到过这道题,一个结论题,问了问队友,结果数论队友直接秒掉了。。

ACCode:

int main(){
	ll a,b;
	cin>>a>>b;
	int flag=0;
	if(a%2&&a>1){
		ll n=(a-1)/2;
		cout<<2*n*n+2*n<<" "<<2*n*n+2*n+1<<endl;
		return 0; 
	} 
	if(a%2==0&&a>=4){
		ll n=a/2;
		cout<<n*n-1<<" "<<n*n+1<<endl;
		return 0;
	} 
	cout<<-1<<endl;
	return 0;
}	

D-羊吃草(暴力+二分匹配)

题目链接:https://ac.nowcoder.com/acm/contest/1083/D

题目大意:中文题

思路:想着先写一个q*400的匈牙利,T了再说。。结果就过了。。

队友完全暴力,q*(r-l+1)*n的查询。。都过了。。

ACCode:

const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

struct Edge1{
    int v,val,nxt;
    Edge1(int _v=0,int _val=0,int _nxt=0){
        v=_v;val=_val;nxt=_nxt;
    }
};
Edge1 Edge[MAXN*MAXN];
int Head[MAXN],Ecnt;
int L[MAXN],R[MAXN];
int Flag[MAXN],Vis[MAXN];
int ql,qr;
int n,q;
 
void Intt(){
    clean(Head,-1);Ecnt=0;
}
void AddEdge(int u,int v,int val){
    Edge[Ecnt]=Edge1(v,val,Head[u]);
    Head[u]=Ecnt++;
}
int DFS(int u){
    for(int i=Head[u];i+1;i=Edge[i].nxt){
        int v=Edge[i].v;//对应编号的羊
        if(Vis[v]==0){
            Vis[v]=1;
            if(Flag[v]==0||DFS(Flag[v])){
                Flag[v]=u;
                return 1;
            }
        }
    }return 0;
}
int main(){
    scanf("%d%d",&n,&q);
    Intt();
    for(int i=1;i<=n;++i) scanf("%d",&L[i]);
    for(int i=1;i<=n;++i) scanf("%d",&R[i]);
    for(int i=1;i<=n;++i){
        for(int j=L[i];j<=R[i];++j){
            AddEdge(i,400+j,1);AddEdge(400+j,i,0);
        }
    }
    while(q--){
        scanf("%d%d",&ql,&qr);
        ql+=400;qr+=400;
        clean(Vis,0);clean(Flag,0);
        int ans=0;
        for(int i=ql;i<=qr;++i){
            clean(Vis,0);
            if(DFS(i)) ans++;
        }printf("%d\n",ans);
    }
}

E-数列(思维+二分)

题目链接:https://ac.nowcoder.com/acm/contest/1083/E

题目大意:中文题

思路:一开始没什么思路,想着贪心,但一直模拟不出贪心策略,后来听见队友来了句二分。。woc!可以二分连续的区间数量,利用贪心思想,尽量平摊区间长度,多余的加前后都行。结果就二分出来了。。。

ans=n-(不连续区间数量),找出最小的不连续递增区间数量就好了。

ACCode:

const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

int n,m;

int Judge(int mid){
	int len=n/mid,del=n%mid;//每段len个元素,剩下del个元素分到前面 
	int Sum=del*(1+len+1)*(len+1)/2+(mid-del)*(1+len)*len/2;
	return Sum;//花费 
}
int main(){
	scanf("%d%d",&n,&m);
	int l=1,r=n,mid;
	while(l<=r){//找到第一个符合要求的mid 
		mid=(l+r)>>1;
		if(Judge(mid)<=m) r=mid-1;//符合要求 
		else l=mid+1;
	}
	int len=n/l,del=n%l;
	for(int i=1;i<=l;++i){
		if(del){
			for(int j=1;j<=len+1;++j) printf("%d ",j);
			--del;
		}
		else{
			for(int j=1;j<=len;++j) printf("%d ",j);
		}
	}
	
}

F-ABCBA(矩阵DP+树剖)

题目链接:https://ac.nowcoder.com/acm/contest/1083/F

题目大意:中文题

思路:有点类似上星期的南昌C?据说也是抄的CF Goodbye2017E。。

用矩阵维护子序列的数量,加个LCA,用树剖维护子序列。注意一下线段树合并。

但是还是没思路(因为这两道都没有写出来QAQ)

赛后看的题解。

https://ac.nowcoder.com/discuss/248083?type=101&order=0&pos=19&page=0

需要注意的是,查询的字符串是v->u的顺序,所以两个链所合并的顺序是不一样的。

但听队友说有一个主席树的解法,常数更小。抽空再看看把QAQ

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
      
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
 
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
      
const int MAXN=3e4+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e4+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

struct SegTree{
	struct Node{
		int l,r;
		int dp[6][6];
		void Intt(){
			for(int i=1;i<=5;++i){
				for(int j=1;j<=5;++j){
					dp[i][j]=0;
				}
			}
		}
		friend Node operator + (Node a,Node b){
			Node ans;
			ans.l=min(a.l,b.l);ans.r=max(a.r,b.r);ans.Intt();
			for(int i=1;i<=5;++i){
				for(int j=1;j<=5;++j){
					ans.dp[i][j]=(a.dp[i][j]+b.dp[i][j])%MOD;
				}
			}
			for(int i=1;i<=5;++i){
				for(int j=1;j<=5;++j){
					for(int k=j+1;k<=5;++k){
						ans.dp[i][k]=(ans.dp[i][k]+a.dp[i][j]*b.dp[j+1][k])%MOD;
					} 
				}
			}return ans;
		}
	};
	Node Tree[MAXN<<2][2];
	
	void PushUp(int rt){
		Tree[rt][0]=Tree[rt<<1][0]+Tree[rt<<1|1][0];
		Tree[rt][1]=Tree[rt<<1|1][1]+Tree[rt<<1][1];
	}
	void Build(int l,int r,int rt,char A[]){
		Tree[rt][0].l=l;Tree[rt][0].r=r;
		if(l==r){
			if(A[l]=='A') Tree[rt][0].dp[1][1]=Tree[rt][0].dp[5][5]=1;
			if(A[l]=='B') Tree[rt][0].dp[2][2]=Tree[rt][0].dp[4][4]=1;
			if(A[l]=='C') Tree[rt][0].dp[3][3]=1;
			Tree[rt][1]=Tree[rt][0];
			return ;
		}int mid=(l+r)>>1;
		Build(l,mid,rt<<1,A);Build(mid+1,r,rt<<1|1,A);
		PushUp(rt);
	}
	Node Query(int ql,int qr,int rt,int opt){
		if(ql<=Tree[rt][opt].l&&Tree[rt][opt].r<=qr){
			return Tree[rt][opt];
		}
		if(qr<=Tree[rt<<1][opt].r) return Query(ql,qr,rt<<1,opt);
		else if(ql>=Tree[rt<<1|1][opt].l) return Query(ql,qr,rt<<1|1,opt);
		else{
			if(opt) return Query(ql,qr,rt<<1|1,opt)+Query(ql,qr,rt<<1,opt);
			else return Query(ql,qr,rt<<1,opt)+Query(ql,qr,rt<<1|1,opt);
		}
	}
	void Show(int rt){
		printf("l=%d r=%d dp:\n",Tree[rt][0].l,Tree[rt][0].r);
		for(int i=1;i<=5;++i){
			for(int j=1;j<=5;++j){
				printf("%d ",Tree[rt][0].dp[i][j]);
			}
		}
		if(Tree[rt][0].l==Tree[rt][0].r) return ;
		Show(rt<<1);Show(rt<<1|1);
	}
};
struct Edge1{
	int v,nxt;
	Edge1(int _v=0,int _nxt=0){
		v=_v;nxt=_nxt;
	}
};
SegTree Seg;
Edge1 Edge[MAXN<<2];
int Head[MAXN],Ecnt;
int Deep[MAXN],Fa[MAXN],Size[MAXN],Son[MAXN];
int Idx[MAXN],Icnt;
int Top[MAXN];
char S[MAXN],B[MAXN];
int n,q;

void Intt(){
	clean(Head,-1);Ecnt=0;
	clean(Deep,0);clean(Fa,-1);clean(Size,0);clean(Son,-1);
	Icnt=0;
}
void AddEdge(int u,int v){
	Edge[Ecnt]=Edge1(v,Head[u]);
	Head[u]=Ecnt++;
}
int DFS1(int u,int fa,int dep){
	Deep[u]=dep;Fa[u]=fa;Size[u]=1;
	int maxson=-1;
	for(int i=Head[u];i+1;i=Edge[i].nxt){
		int v=Edge[i].v;
		if(v==fa) continue;
		Size[u]+=DFS1(v,u,dep+1);
		if(Size[v]>maxson){
			maxson=Size[v];Son[u]=v;
		}
	}return Size[u];
}
void DFS2(int u,int topfa){
	Idx[u]=++Icnt;Top[u]=topfa;B[Idx[u]]=S[u];
	if(Son[u]==-1) return ;
	DFS2(Son[u],topfa);
	for(int i=Head[u];i+1;i=Edge[i].nxt){
		int v=Edge[i].v;
		if(Idx[v]==0) DFS2(v,v);
	}
}
int QueryCnt(int a,int b){//b->a
	int aa=(MAXN<<2)-1,bb=(MAXN<<2)-2;
	Seg.Tree[aa][0].Intt();Seg.Tree[bb][0].Intt();
	while(Top[a]!=Top[b]){
		if(Deep[Top[a]]<Deep[Top[b]]){//b向上提 
			Seg.Tree[bb][0]=Seg.Tree[bb][0]+Seg.Query(Idx[Top[b]],Idx[b],1,1);
			b=Fa[Top[b]];
		}
		else{//a向上提 
			Seg.Tree[aa][0]=Seg.Query(Idx[Top[a]],Idx[a],1,0)+Seg.Tree[aa][0];
			a=Fa[Top[a]];
		}
	}
	if(Deep[a]<Deep[b]){
		Seg.Tree[bb][0]=(Seg.Tree[bb][0]+Seg.Query(Idx[a],Idx[b],1,1))+Seg.Tree[aa][0];
		return Seg.Tree[bb][0].dp[1][5]%MOD;
	}
	else{
		Seg.Tree[aa][0]=(Seg.Tree[bb][0]+Seg.Query(Idx[b],Idx[a],1,0))+Seg.Tree[aa][0];
		return Seg.Tree[aa][0].dp[1][5]%MOD;
	}
}
int main(){
	Intt();
	scanf("%d%d",&n,&q);
	scanf("%s",S+1);
	for(int i=1;i<n;++i){
		int u,v;scanf("%d%d",&u,&v);
		AddEdge(u,v);AddEdge(v,u);
	}DFS1(1,-1,1);DFS2(1,1);
//	for(int i=1;i<=n;++i) printf("Idx[%d]=%d\n",i,Idx[i]); 
	Seg.Build(1,n,1,B);
	for(int i=1;i<=q;++i){
		int a,b;scanf("%d%d",&a,&b);
		printf("%d\n",QueryCnt(a,b)%MOD);
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值