牛客练习赛70

A.二分答案,用前缀和判断区间内字符数量是否符合条件

#include<bits/stdc++.h>
#define N 100010
#define ll long long
using namespace std;
int qt;
int n;
char s[N];
int c[N][30];
string a="puleyaknoi";
int chk(int mid){
	for(int i=1;i+mid-1<=n;i++){
		int bo=0;
		for(int j=0;j<10;j++){
			if(c[i+mid-1][a[j]-'a']-c[i-1][a[j]-'a']<1){
				bo=1;break;
			}
		}
		if(bo==0)return 1;
	}
	return 0;
}
int main(){
	int qt;
	scanf("%d",&qt);
	while(qt--){
			scanf("%s",s+1);
			n=strlen(s+1);
			if(n<10){
				printf("-1\n");continue;
			}
			for(int i=0;i<=n;i++)
			for(int j=0;j<=25;j++)c[i][j]=0;
			for(int i=1;i<=n;i++){
				for(int j=0;j<=25;j++)c[i][j]=c[i-1][j];
				c[i][s[i]-'a']=c[i-1][s[i]-'a']+1;
			}
			int l=10,r=n,ans=-1;
			while(l<=r){
				int mid=(l+r)>>1;
				if(chk(mid)){
					r=mid-1;ans=mid;
				}else l=mid+1;
			}
			printf("%d\n",ans);
		}
	return 0;
}

B.记录每个字符的下一个最近的位置,暴力计算即可

#include<bits/stdc++.h>
#define N 100010
#define ll long long
using namespace std;
int qt;
int n;
char s[N];
int c[30];
int f[N];
string a="puleyaknoi";
int chk(int x){
	if(s[x]!='p')return 1e9;
	int cnt=0,ret=0,t=x;
	while(x&&x<=n){
		x=f[x];cnt++;
		ret=max(ret,x);
	}
	if(cnt==10)return ret-t+1;
	return 1e9;
}
int main(){
	int qt;
	scanf("%d",&qt);
	while(qt--){
			scanf("%s",s+1);
			n=strlen(s+1);
			if(n<10){
				printf("-1\n");continue;
			}
			for(int i=0;i<=n;i++)f[i]=0;
			for(int i=0;i<=25;i++)c[i]=0;
			for(int i=n;i>=1;i--){
				int t=-1;
				for(int j=0;j<10;j++)
				if(s[i]==a[j])t=j;
				if(t==-1||t==9){
					c[s[i]-'a']=i;
					continue;
				}
			//	if(i==n-2)cout<<c[a[t+1]-'a']<<endl;
				f[i]=c[a[t+1]-'a'];
				c[s[i]-'a']=i;
			}
			//for(int i=1;i<=n;i++)cout<<f[i]<<" ";cout<<endl;
			int ans=1e9;
			for(int i=1;i+9<=n;i++)
			ans=min(ans,chk(i));
			if(ans==1e9)printf("-1\n");
			else printf("%d\n",ans);
		}
	return 0;
}

C.链接:https://ac.nowcoder.com/acm/problem/211597

当k很大时mu[x]=0或在-1,1之间循环,找到循环节即可

注意循环节可能不从一开始

#include<bits/stdc++.h>
#define N 100010
#define ll long long
using namespace std;
int qt;
ll n,k;
map<ll,int>v;
ll a[N]; 
ll get(ll a){
    ll x=a,tmp=a;
    int cnt=0,now=0;
    for(ll j=2;j*j<=x;j++){
        now=0;
        if(x%j==0){
            while(x%j==0) now++,x/=j;
            if(now>1) return 0;
            cnt++;
        }
    }
    if(x!=1) cnt++;
    return (cnt&1)?-1:1;
}
ll f(ll x){
	return x+get(x);
}
int main(){
	scanf("%d",&qt);
	while(qt--){
		scanf("%lld%lld",&n,&k);
		v.clear();
		int cnt=0,l,r;
		for(int i=1;i<=k;i++){
			n=f(n);//cout<<n<<endl;
			if(v.find(n)==v.end())a[++cnt]=n;
			else {
				l=v[n];r=i-1;break;
			}
			v[n]=i;
		}
		
		if(k<l)printf("%d\n",a[k]);
		else {
			a[l-1]=a[r];
			printf("%lld\n",a[(k-l+1)%(r-l+1)+l-1]);
		}
	}
	return 0;
}

D.

小牛牛在暑假的时候开始种树。

但他种树的方式比较奇怪。他会先拿出一些有标号的点,然后他会有一些操作,分为3种:添加一条边,切断一条边,询问他有多少个大小不为一的树。当然,年幼的小牛牛记性不是很好,他有时会连出重边,有时也会切断根本不存在的边。当然,对于这样的操作,无视就可以了。

分析:

当添边时分为

1.当deg[x]=0,deg[y]=0时,答案ans++;

2.当deg[x]>0,deg[y]>0时,ans--;

删边时:

1.当deg[x]=1,deg[y]=1时,ans--;

2.当deg[x]>1,deg[y]>1时,ans++;

用set维护每个点所连的点,便于操作。

#include<bits/stdc++.h>
#define N 100010
#define ll long long
using namespace std;
int a[N*2],u[N],v[N],op[N];
set<int>s[N*2]; 
int main(){
	int n,cnt=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&op[i]);
		if(op[i]==1||op[i]==2){
			scanf("%d%d",&u[i],&v[i]);
			a[++cnt]=u[i];a[++cnt]=v[i];
		}
	}
	sort(a+1,a+cnt+1);
	cnt=unique(a+1,a+cnt+1)-a-1;
	for(int i=1;i<=n;i++){
		if(op[i]==3)continue;
		u[i]=lower_bound(a+1,a+cnt+1,u[i])-a;
		v[i]=lower_bound(a+1,a+cnt+1,v[i])-a;
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(op[i]==1){
			if(u[i]==v[i])continue;
			if(u[i]>v[i])swap(u[i],v[i]);
			if(s[u[i]].count(v[i]))continue;
			if(s[u[i]].size()==0&&s[v[i]].size()==0)ans++;
			else if(s[u[i]].size()>0&&s[v[i]].size()>0)ans--;
			s[u[i]].insert(v[i]);
			s[v[i]].insert(u[i]);
		}else if(op[i]==2){
			if(u[i]==v[i])continue;
			if(u[i]>v[i])swap(u[i],v[i]);
			if(s[u[i]].count(v[i])==0)continue;
			if(s[u[i]].size()==1&&s[v[i]].size()==1)ans--;
			else if(s[u[i]].size()>1&&s[v[i]].size()>1)ans++;
			s[u[i]].erase(v[i]);
			s[v[i]].erase(u[i]);
		}else printf("%d\n",ans);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值