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;
}