2019牛客暑期多校训练营(第三场)
Solutions
B:Crazy Binary String
题意:
给出$01$串,询问最长的“$01$数量相等”的字串和子序列。
思路:
字串的话,把$0$变成$-1$,求前缀和,如果$sum[r]-sum[l-1]=0$,说明$01$数量相等。
所以可以跑一遍,$map$找到符合的位置。取最大。
子序列:显示是$2{\ast}min(num[zero],num[one])$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+10; 4 5 int n,sum[maxn]; 6 char s[maxn]; 7 8 map<int,int> mp; 9 int main() { 10 scanf("%d",&n); 11 scanf("%s",s+1); 12 int one=0,zero=0; 13 for(int i=1;i<=n;i++) { 14 if(s[i]=='1') one++; 15 else zero++; 16 sum[i]=sum[i-1]+(s[i]=='1'?1:-1); 17 } 18 mp[0]=0; 19 int ans=0; 20 for(int i=1;i<=n;i++) { 21 if(mp.find(sum[i])!=mp.end()) { 22 ans=max(ans,i-mp[sum[i]]); 23 } else mp[sum[i]]=i; 24 } 25 printf("%d %d\n",ans,min(one,zero)*2); 26 return 0; 27 }
D:Big Integer
题意:
$A\left(i^j\right)$表示$i^j$个$1$,求$A\left(i^j\right)\equiv0mod\ p$有多少对
- $1\leq\ i\leq\ n$
- $1\leq\ j\leq\ m$
- $p$为质数
思路:
$1111$可写为$\frac{{10}^n-1}9$,则可以转化为
$$\frac{{10}^n-1}9\equiv0mod\ p$$
$$\left({10}^n-1\right)\ast\ inv\left(9\right)\equiv0mod\ p$$
若$p=2或5$,明显答案为$0$,
若$p$与$9$互质,$inv\left(9\right)\neq0$,
所以 $${10}^n-1\equiv0mod\ p$$
即 $${10}^n\equiv1mod\ p$$
由欧拉定理得$\varphi\left(p\right)$必定满足,但不一定是最小的。
所以我们可以求得最小循环节$x$,复杂度$O\left(\sqrt{p}log\ p\right)$
转化为有多少对$x\mid{i^j}$
考虑固定$j$
对$x$进行质因数分解,
$x=p_1^{ax_1}\ast\ p_2^{ax_2}\ast\ldots\ast\ p_k^{ax_k}$
那么$i$的每种质因子的个数至少有$\lceil\frac{ax}j\rceil$
令$g=p_1^{\lceil\frac{ax_1}j\rceil}\ast\ p_2^{\lceil\frac{ax_2}j\rceil}\ast\ldots\ast\ p_k^{\lceil\frac{ax_k}j\rceil}$,$g\mid\ i$
在$n$的范围内有$\lceil\frac{n}g\rceil$个。
$g$随着$j$的改变而改变。
令$mx=max\left(ax_1,ax_2,\ldots\,ax_k\right)$,若$j\geq\ mx$,对后面的$j$,影响不会改变。有$\left(m-j\right)\ast\lceil\frac{n}g\rceil$个。
考虑$p=3$与$9$不互质,根据整除$3$的性质,有$n\setminus3\ast\ m$个,(数字之和为$3$的倍数)
1 //#define DEBUG 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int N=100010; 5 const int inf=0X3f3f3f3f; 6 const long long INF = 0x3f3f3f3f3f3f3f3f; 7 const double eps = 1e-6; 8 const double pi = acos(-1.0); 9 //const int mod = 1000000007; 10 typedef long long ll; 11 12 int q_pow(int a,int b,int p) { 13 int tmp=a%p; 14 int ans=1; 15 while(b) { 16 if(b&1) ans=1ll*ans*tmp%p; 17 tmp=1ll*tmp*tmp%p; 18 b=b>>1; 19 } 20 return ans; 21 } 22 23 int main() { 24 #ifdef DEBUG 25 freopen("in.txt","r",stdin); 26 #endif 27 int _,p,n,m; 28 for(scanf("%d",&_);_;_--) { 29 scanf("%d%d%d",&p,&n,&m); 30 if(p==2||p==5) { 31 puts("0"); 32 continue; 33 } 34 if(p==3) { 35 printf("%lld\n",1ll*n/3*m); 36 continue; 37 } 38 int D=p-1,x=D; 39 for(int i=1;i*i<=D;i++) { 40 if(D%i) continue; 41 if(q_pow(10,i,p)==1) x=min(x,i); 42 if(q_pow(10,D/i,p)==1) x=min(x,D/i); 43 } 44 vector<pair<int,int>> factor; 45 int maxx=-1; 46 for(int i=2;i*i<=x;i++) { 47 if(x%i==0) { 48 int cnt=0; 49 while(x%i==0) { 50 cnt++; 51 x/=i; 52 } 53 maxx=max(maxx,cnt); 54 factor.push_back({i,cnt}); 55 } 56 } 57 if(x>1) factor.push_back({x,1}); 58 // puts("***"); 59 ll ans=0; 60 for(int j=1;j<=m;j++) { 61 ll g=1; 62 for(auto it:factor) { 63 int t=(it.second+j-1)/j; 64 while(t--) { 65 g=g*(it.first); 66 } 67 } 68 ans+=n/g; 69 if(j>=maxx) { 70 ans+=(m-j)*(n/g); 71 break; 72 } 73 } 74 printf("%lld\n",ans); 75 } 76 }
F:Planting Trees
题意:
给出$n\times\ n$的矩阵,和一个$m$,求最大的子矩阵,要求子矩阵的最大值和最小值差值小于等于$m$。
思路:
枚举上下边界,然后单调队列处理左右边界。
把每一列的最大值、最小值记下来,然后用单调队列维护,如果差值大于$m$就往右移动。
1 //#define DEBUG 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int N=100010; 5 const int inf=0X3f3f3f3f; 6 const long long INF = 0x3f3f3f3f3f3f3f3f; 7 const double eps = 1e-6; 8 const double pi = acos(-1.0); 9 const int mod = 1000000007; 10 typedef long long ll; 11 12 int a[610][610]; 13 int b1[610],b2[610]; 14 int q1[610],q2[610],l1,l2,r1,r2; 15 16 int main() { 17 #ifdef DEBUG 18 freopen("in.txt","r",stdin); 19 #endif 20 int _; 21 for(scanf("%d",&_);_;_--) { 22 int n,m,ans=0; 23 scanf("%d%d",&n,&m); 24 for(int i=1;i<=n;i++) { 25 for(int j=1;j<=n;j++) 26 scanf("%d",&a[i][j]); 27 } 28 for(int i=1;i<=n;i++) {//上界 29 for(int j=1;j<=n;j++) b1[j]=b2[j]=a[i][j]; //初始化 30 for(int j=i;j<=n;j++) {// 下界 31 for(int k=1;k<=n;k++) b1[k]=min(b1[k],a[j][k]),b2[k]=max(b2[k],a[j][k]); //维护 32 l1=l2=1;r1=r2=0; 33 if((j-i+1)*n<=ans) continue; //剪枝 34 for(int w=1,k=1;k<=n;k++) {//左右边界 35 while(l1<=r1&&b1[q1[r1]]>=b1[k]) --r1; //单调增 36 q1[++r1]=k; 37 while(l2<=r2&&b2[q2[r2]]<=b2[k]) --r2; //单调减 38 q2[++r2]=k; 39 while(w<=k&&b2[q2[l2]]-b1[q1[l1]]>m) { 40 w++; 41 while(l1<=r1&&q1[l1]<w) ++l1; 42 while(l2<=r2&&q2[l2]<w) ++l2; 43 } 44 if((j-i+1)*(n-w+1)<=ans) break; 45 if(w<=k) ans=max(ans,(j-i+1)*(k-w+1)); 46 } 47 } 48 } 49 printf("%d\n",ans); 50 } 51 }
G:Removing Stones
题意:
给出$n$堆石子,每次可以选非空且不同的两堆,然后各取一个石子,此操作可以进行任意次,很明显只要数量为偶数,Mark必定可以完成,现在问你有多少个区间$\left(l,r\right)$,Mark可以完成。
思路:
很明显题目可以转化为求多少个这样的区间$\left(l,r\right)$:
$$max_{l\leq\ i\leq\ r}\ \leq\ \sum_{i=l}^ra_i$$
可以用$RMQ$查询最大值,前缀和查询区间和,考虑计数,可以分治。
每次在小区间枚举一个端点,在大区间二分另一个端点
比如左边区间小,就在右边二分找另一个端点。
记最值下标为$k$,则需满足$2\ast\ a[k]\leq\ sum[r]-sum[l-1]$
枚举左边,所以$sum[r]\geq\ 2\ast\ a[k]+sum[l-1]$,找第一个大于$2\ast\ a[k]+sum[l-1]$的位置
若右边区间小,同理。
1 //#define DEBUG 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int N=300010; 5 const int inf=0X3f3f3f3f; 6 const long long INF = 0x3f3f3f3f3f3f3f3f; 7 const double eps = 1e-6; 8 const double pi = acos(-1.0); 9 const int mod = 1000000007; 10 typedef long long ll; 11 12 ll sum[N],ans; 13 int n,lg[N],a[N],dp[N][20]; 14 15 void rmq() { 16 lg[0]=-1; 17 for(int i=1;i<=n;i++) { 18 lg[i]=(i&(i-1))?lg[i-1]:lg[i-1]+1; 19 dp[i][0]=i; 20 } 21 for(int j=1;j<=lg[n];j++) { 22 for(int i=1;i+(1<<j)-1<=n;i++) 23 dp[i][j]=a[dp[i][j-1]]>a[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1]; 24 } 25 } 26 27 int query(int l,int r) { 28 int k=lg[r-l+1]; 29 return (a[dp[l][k]]>a[dp[r-(1<<k)+1][k]])?dp[l][k]:dp[r-(1<<k)+1][k]; 30 } 31 32 33 int bs1(int l,int r,ll v) { 34 if(sum[r]<v) return r+1; 35 int res=-1,mid; 36 while(l<=r) { 37 mid=(l+r)>>1; 38 if(sum[mid]>=v) { 39 res=mid; 40 r=mid-1; 41 } else l=mid+1; 42 } 43 return res; 44 } 45 46 int bs2(int l,int r,ll v) { 47 if(sum[l-1]>v) return l-1; 48 int res=-1,mid; 49 while(l<=r) { 50 mid=(l+r)>>1; 51 if(sum[mid-1]<=v) { 52 res=mid; 53 l=mid+1; 54 } else r=mid-1; 55 } 56 return res; 57 } 58 59 void work(int l,int r) { 60 if(r-l+1<=1) return ; 61 if(r-l+1==2) { 62 if(a[l]==a[r]) ans++; 63 return; 64 } 65 int k=query(l,r); 66 if(k-l<r-k) { 67 for(int i=l;i<=k;i++) { 68 int j=bs1(k,r,2ll*a[k]+sum[i-1]); 69 ans+=(r-j+1); 70 } 71 } else { 72 for(int i=k;i<=r;i++) { 73 int j=bs2(l,k,sum[i]-2ll*a[k]); 74 ans+=(j-l+1); 75 } 76 } 77 work(l,k-1); 78 work(k+1,r); 79 } 80 81 82 int main() { 83 #ifdef DEBUG 84 freopen("in.txt","r",stdin); 85 #endif 86 int _; 87 for(scanf("%d",&_);_;_--) { 88 scanf("%d",&n); 89 sum[0]=0; 90 for(int i=1;i<=n;i++) { 91 scanf("%d",&a[i]); 92 sum[i]=sum[i-1]+a[i]; 93 } 94 rmq(); 95 ans=0; 96 work(1,n); 97 printf("%lld\n",ans); 98 } 99 }
H:Magic Line
题意:
给出平面上$n$个点,要画一条直线,把这些点划分为数量相等的两部分。
思路:
对$x,y$双关键字排序,然后取中间(偏左)的那个点,然后稍微偏一点即可。(两个纵坐标加的数不要互为相反数,***钻一点)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=10010; 4 5 int _,n; 6 struct Point{ 7 int x,y; 8 bool operator < (const Point &b)const { 9 return x<b.x||(x==b.x&&y<b.y); 10 } 11 }p[maxn]; 12 13 int main() { 14 for(scanf("%d",&_);_;_--) { 15 scanf("%d",&n); 16 for(int i=1;i<=n;i++) { 17 scanf("%d%d",&p[i].x,&p[i].y); 18 } 19 sort(p+1,p+n+1); 20 printf("%d %d %d %d\n",p[n/2].x-1,p[n/2].y+99999999+1,p[n/2].x+1,p[n/2].y-99999999); 21 } 22 }
J:LRU management
题意:
给出一个容器,最多放$m$块,然后一些列操作,
$opt=1$,不改变序列,
如果能找到,根据v的值,输出它前后或者本身的$data$。
$opt=0$,改变序列,
如果能找到,输出它的$data$,并把它移动到末尾,
如果找不到,输出$v$值,并把它加入末尾。
如果某一步大于容器大小,就把第一个移出。
思路:
数组模拟双向链表+$trie$树映射 或 $unorderedmap+list$ 具体见题解代码
(数组模拟就是爽)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e5+10; 4 5 int trie[N*10][10],pointer[N*10]; 6 int pos[N*10],tot; 7 8 int L[N],R[N],data[N]; 9 int st,ed,size; 10 11 void Insert(int x,int v) { 12 data[x]=v; 13 if(!st) st=ed=x; 14 else L[R[ed]=x]=ed,ed=x; 15 ++size; 16 } 17 18 void Erase(int x) { 19 int pre=L[x],bak=R[x]; 20 if(pre) R[pre]=bak; 21 if(bak) L[bak]=pre; 22 data[x]=L[x]=R[x]=0; 23 if(st==x) st=bak; 24 if(ed==x) ed=pre; 25 --size; 26 } 27 28 void trieInsert(char* s,int it) { 29 int p=1; 30 for(int i=0;s[i];i++) { 31 int &t=trie[p][s[i]-'0']; 32 if(!t) pos[t=++tot]=0;p=t; 33 } 34 pos[pointer[it]=p]=it; 35 } 36 37 int trieFind(char *s) { 38 int p=1; 39 for(int i=0;s[i]&&p;i++) { 40 int t=trie[p][s[i]-'0']; 41 p=t; 42 } 43 return pos[p]; 44 } 45 46 47 int main() { 48 int _; 49 for(scanf("%d",&_);_;_--) { 50 int q,m; 51 scanf("%d%d",&q,&m); 52 pointer[tot=1]=0; 53 st=ed=size=0; 54 int number=0; 55 while(q--) { 56 int opt,v;char s[12]; 57 scanf("%d%s%d",&opt,s,&v); 58 int it=trieFind(s); 59 if(opt==1) { 60 if(it==0||L[it]==0&&v==-1||R[it]==0&&v==1) 61 puts("Invalid"); 62 else { 63 if(v==-1) it=L[it]; 64 if(v==1) it=R[it]; 65 printf("%d\n",data[it]); 66 } 67 } else { 68 if(it) { 69 v=data[it]; 70 Erase(it); 71 } else if(size==m){ 72 pos[pointer[st]]=0; 73 Erase(st); 74 } 75 Insert(++number,v); 76 trieInsert(s,number); 77 printf("%d\n",v); 78 } 79 } 80 for(int i=1;i<=tot;i++) { 81 for(int k=0;k<10;k++) trie[i][k]=0; 82 pos[i]=0; 83 } 84 while(st) Erase(st); 85 } 86 return 0; 87 }
$unorderedmap+list$
学到了$map$里放迭代器,用$decltype(list)::iterator$,以及$prev和next$
1 //#define DEBUG 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int N=100010; 5 const int inf=0X3f3f3f3f; 6 const long long INF = 0x3f3f3f3f3f3f3f3f; 7 const double eps = 1e-6; 8 const double pi = acos(-1.0); 9 const int mod = 1000000007; 10 typedef long long ll; 11 12 int main() { 13 #ifdef DEBUG 14 freopen("in.txt","r",stdin); 15 #endif 16 int _; 17 for(scanf("%d",&_);_;_--) { 18 list<pair<ll,ll>> list; 19 unordered_map<ll,decltype(list)::iterator> map; 20 int q,m; 21 scanf("%d%d",&q,&m); 22 while(q--) { 23 int opt; 24 ll v; 25 char s[15]; 26 scanf("%d%s%lld",&opt,s,&v); 27 ll id; 28 sscanf(s,"%lld",&id); 29 id+=1ll*10000000000*strlen(s)+id; 30 if(opt==0) { 31 auto it=map.find(id); 32 if(it!=map.end()) { 33 auto p=map[id]; 34 v=p->second; 35 //map.erase(p->first); 36 list.erase(p); 37 } 38 list.push_back({id,v}); 39 map[id]=prev(list.end()); 40 if(list.size()>m) { 41 auto p=list.begin(); 42 map.erase(p->first); 43 list.pop_front(); 44 } 45 printf("%lld\n",v); 46 } else { 47 bool ok=true; 48 auto it=map.find(id); 49 if(it==map.end()) ok=false; 50 else { 51 auto p=map[id]; 52 if(v==-1) { 53 if(p==list.begin()) ok=false; 54 else printf("%lld\n",prev(p)->second); 55 } else if(v==1) { 56 if(next(p)==list.end()) ok=false; 57 else printf("%lld\n",next(p)->second); 58 } else printf("%lld\n",p->second); 59 } 60 if(!ok) puts("Invalid"); 61 } 62 } 63 } 64 }