Educational Codeforces Round 81 (Rated for Div. 2)
题目大意:
给你一个字符串s,s是一个01串,则t=ssss... 即为s的无限循环。
问在无限长的字符串t中,有多少个前缀满足 : 0的个数 - 1的个数==x (x给出)
思路:
这个题目我开始以为是一个模拟题,这个就让我的方向上出现了很大的错误,
我认为是模拟题,所以我想办法去模拟这个无限长的串,但没写出来。
后来查了题解才发现是一个数学题。
设bal(i) 表示前缀长度为 i 的0的个数 - 1的个数的值。
对于研究有限长的 s 串,可以发现有两种情况。
第一种就是 bal(n) == 0,也就是说s串0和1的个数相同,这个时候就判断一下是不是存在bal(i)==x
如果存在,那么就输出 -1
第二种情况是 bal(i)!=0 这个时候就是解一个方程 x==k*bal(n)+bal(i)
从头往后遍历 判断对于每一个bal(i) 是否存在一个非负数 x==k*bal(n)+bal(i)
为什么说只要遍历s串就可以了呢,t串可是包含了无数个s串的。
这个很简单,因为s串之后都只是s串的简单的重复,这个说明bal(i)可以拆成 bal(i-n)+bal(n)
只需要算s串即可,否则就会算重复了。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; char s[maxn]; int main(){ int t; scanf("%d",&t); while(t--){ int n,x; scanf("%d%d%s",&n,&x,s); int sum=0; for(int i=0;i<n;i++) { if(s[i]=='0') sum++; else sum--; } int flag=0,ans=0,cur=0; for(int i=0;i<n;i++){ if(sum==0){ if(cur==x) flag=1; } else if(abs(x-cur)%sum==0){ if((x-cur)/sum>=0) ans++; } if(s[i]=='0') cur++; else cur--; } if(flag) ans=-1; printf("%d\n",ans); } return 0; }
题目大意:给两个数 a,m 求gcd(a,m)==gcd(a+x,m) 并且 0<=x<m,求有多少个x满足条件。
做法,有两种:
第一种:
因为gcd(a,m)==g 所以gcd(a+x,m)==g
又因为a%g==0 (a+x)%g==0 所以 x%g==0
gcd((a+x)/g,m/g)==1 a1=(a+x)/g,b1=m/g
所以 a1的范围是[a/g,(a+m-1)/g] b1 是一个常数,
解决在一个区间内和b1互质的数的个数方法参见--->欧拉函数
第二种:
gcd(a+x,m)==gcd((a+x)%m,m) a1=(a+x)%m
所以a1的范围是[0,m-1]
gcd(a1,m)==g 所以 gcd(a1/g,m/g)==1
a2=a1/g 所以就相当于求欧拉函数
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+10; typedef long long ll; ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); } int v[maxn],isp[maxn],m; void init1() { for(int i=2;i<maxn;i++){ if(v[i]==0){ isp[m++]=i; v[i]=i; } for(int j=0;j<m;j++){ if(v[i]<isp[j]||i*isp[j]>maxn) break; v[i*isp[j]]=isp[j]; } } } int main(){ int t; init1(); scanf("%d",&t); while(t--){ ll a,b; scanf("%lld%lld",&a,&b); ll g=gcd(a,b); ll cur=b/g,lc=a/g,rc=(a+b-1)/g; for(int i=0;i<m;i++){ int v=isp[i]; if(cur%v==0){ lc=lc/v*(v-1),rc=rc/v*(v-1); while(cur%v==0) cur/=v; } } if(cur!=1) lc=lc/cur*(cur-1),rc=rc/cur*(cur-1); printf("%lld\n",rc-lc); } return 0; }
题目大意:将一个序列进行划分,然后再进行交换,每一个数交换的代价为a[i],
两种操作之后使得到的两段,第一段的数在区间[1,k] 第二段的数在区间[k+1,n],问最小的代价是多大?
一开始没什么思路,然后问了lj才会写的。
因为不同的划分位置会有不同的结果,所以我们一定要对划分的位置进行讨论,这样就有1e5的复杂度了。
然后就是每一次划分我们都应该把最小代价求出。
差不多就是这么做的把,自己再对着样例比划比划就可以写出来了。
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+10; typedef long long ll; //Ë㻨·Ñ×¢Ò⿪ long long ll mins[maxn*4],lazy[maxn*4],pre[maxn]; ll p[maxn],a[maxn]; void push_up(int id){ mins[id]=min(mins[id<<1],mins[id<<1|1]); } void build(int id,int l,int r){ lazy[id]=0; if(l==r){ mins[id]=pre[l]; return ; } int mid=(l+r)>>1; build(id<<1,l,mid); build(id<<1|1,mid+1,r); push_up(id); } void push_down(int id){ if(lazy[id]==0) return ; mins[id<<1]+=lazy[id]; mins[id<<1|1]+=lazy[id]; lazy[id<<1]+=lazy[id]; lazy[id<<1|1]+=lazy[id]; lazy[id]=0; } void update(int id,int l,int r,int x,int y,ll val){ if(x>r||y<l) return ; if(x<=l&&y>=r){ mins[id]+=val; lazy[id]+=val; return ; } push_down(id); int mid=(l+r)>>1; if(x<=mid) update(id<<1,l,mid,x,y,val); if(y>mid) update(id<<1|1,mid+1,r,x,y,val); push_up(id); } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&p[i]); for(int i=1;i<=n;i++) scanf("%lld",&a[i]),pre[p[i]]=a[i]; for(int i=1;i<=n;i++) pre[i]=pre[i]+pre[i-1]; build(1,1,n); ll ans=min(a[1],a[n]); for(int i=1;i<n;i++){ update(1,1,n,1,p[i]-1,a[i]); update(1,1,n,p[i],n,-a[i]); ans=min(ans,mins[1]); // printf("i=%d ans=%lld\n",i,ans); } printf("%lld\n",ans); return 0; }