传送门
B.Crazy Binary String(前缀和)
•题意
给你一个只包含 0,1 的串 s;
求满足 0 与 1 的个数相同的子串和子序列;
输出这两个串的最大长度;
•思路
找01个数相同的子串,类似于这个(A题),(话说比那个简单有没有
根据前缀和求增量,如果增量相同的话,那这段区间里01个数就相同,然后每次取最大的ans
找01个数相同的子序列,那肯定是01中个数少的那个所组成的,输出两倍就好了
•代码
View Code1 #include2 using namespace std; 3 map<int,int> mp; 4 char s[10005]; 5 int zero,one; 6 int ans; 7 int main() 8 { 9 int n; 10 cin>>n; 11 mp[0]=0; 12 for(int i=1;i<=n;i++) 13 { 14 cin>>s[i]; 15 if(s[i]=='0') 16 zero++; 17 else 18 one++; 19 if(mp.count(zero-one)) 20 ans=max(ans,i-mp[zero-one]); 21 else 22 mp[zero-one]=i; 23 } 24 cout< ' '< 2<<endl; 25 }
D.Big Integer(数论)
•题意
$A_{n}=\underbrace{1......1}_{n}$
$A_{i^j}$ ≡ 0 (mod p)的个数,其中 1 ≤ i ≤ n , 1 ≤ j ≤ m;
•思路
写起来有点麻烦...具体戳这里吧
防止忘记推导过程,记一下重点...
•代码
View Code1 #include2 using namespace std; 3 #define ll long long 4 int p,n,m; 5 ll ans; 6 int P[105]; 7 int x[105]; 8 9 ll qpow(ll a,ll b,ll mod) 10 { 11 ll res=1; 12 while(b) 13 { 14 if(b&1) 15 res=res*a%mod; 16 a=a*a%mod; 17 b>>=1; 18 } 19 return res; 20 } 21 22 int F()//因子分解得到最小循环节d,Max(d)=p-1 23 { 24 int d=p-1; 25 for(int i=1;i*i<=p-1;i++) 26 { 27 if((p-1)%i==0) 28 { 29 if(qpow(10,i,p)==1) 30 d=min(d,i); 31 if(qpow(10,(p-1)/i,p)==1) 32 d=min(d,(p-1)/i); 33 } 34 } 35 return d; 36 } 37 38 int PrimeF(int d)//质因子分解 得到pi,xi,k 39 { 40 int k=0; 41 for(int i=2;i*i<=d;i++) 42 { 43 if(d%i==0) 44 { 45 P[++k]=i; 46 x[k]=0; 47 } 48 while(d%i==0) 49 { 50 x[k]++; 51 d/=i; 52 } 53 } 54 if(d!=1) 55 { 56 P[++k]=d; 57 x[k]=1; 58 } 59 return k; 60 } 61 62 void Slove() 63 { 64 int d=F(); 65 int k=PrimeF(d); 66 int maxX=*max_element(x+1,x+1+k); 67 for(int j=1;j<=m;j++)//固定j,求个数 68 { 69 ll g=1; 70 for(int i=1;i<=k;i++) 71 g*=qpow(P[i],(x[i]+j-1)/j,p); //p^(x+j-1)/j 72 ans+=n/g; 73 74 if(j>=maxX)//此时g为最小不再变化,共有(n/g)*(m-j)种 75 { 76 ans+=n/g*(m-j); 77 break; 78 } 79 } 80 } 81 82 int main() 83 { 84 int t; 85 cin>>t; 86 while(t--) 87 { 88 cin>>p>>n>>m; 89 ans=0; 90 if(p==3) 91 ans=n/3*m; 92 else if(p==2||p==5) 93 ans=0; 94 else 95 Slove(); 96 cout< endl; 97 } 98 }
F.Planting Trees(单调队列优化)
•题意
在一块 n×n 的土地上植树;
第(i,j) 块有棵高为 a[ i ][ j ] 树;
必须要找一片满足任意两棵树的高度差都不超过 m 的矩形区域;
并且想要这个矩形区域面积尽可能大;
求出这个矩形区域,输出这个矩形区域包含的树的个数;
•思路
注意:
单调队列代码实现时
①要分清楚值和索引,蟹蟹可爱的猪猪帮我找bug,此致敬礼!
②在出队列时(head++时)要注意队列非空,借鉴猪猪的经验!
•代码
View Code1 #include2 using namespace std; 3 const int maxn=505; 4 int a[maxn][maxn]; 5 int demin[maxn]; 6 int demax[maxn]; 7 int Min[maxn]; 8 int Max[maxn]; 9 int n,m; 10 int ans; 11 12 int main() 13 { 14 int t; 15 cin>>t; 16 while(t--) 17 { 18 cin>>n>>m; 19 ans=0; 20 for(int i=1;i<=n;i++) 21 for(int j=1;j<=n;j++) 22 cin>>a[i][j]; 23 24 25 for(int i=1;i<=n;i++) 26 { 27 //初始化 28 for(int j=1;j<=n;j++) 29 Min[j]=Max[j]=a[i][j]; 30 31 //枚举上下边界 32 //从第i行到第k行求最优解 33 for(int k=i;k<=n;k++) 34 { 35 for(int j=1;j<=n;j++) 36 { 37 //维护当前上下边界第i到第k行 每列的最大值最小值 38 Min[j]=min(Min[j],a[k][j]); 39 Max[j]=max(Max[j],a[k][j]); 40 } 41 42 int l=1,r=1; 43 int headmin=1,headmax=1; 44 int tailmin=0,tailmax=0; 45 //枚举右边界r 46 //维护两个单调队列 47 while(r<=n) 48 { 49 //单调递增队列,头是最小值 50 while(headmin<=tailmin&&Min[demin[tailmin]]>=Min[r]) 51 tailmin--; 52 demin[++tailmin]=r; 53 //单调递减队列,头是最大值 54 while(headmax<=tailmax&&Max[demax[tailmax]]<=Max[r]) 55 tailmax--; 56 demax[++tailmax]=r; 57 58 //每个右边界确定后(也就是在此右边界下) 59 //找符合条件并使面积尽量大的左边界 60 while(l<=r&&Max[demax[headmax]]-Min[demin[headmin]]>m) 61 { 62 l++; 63 while(headmax<=tailmax&&demax[headmax]<l) 64 headmax++; 65 while(headmin<=tailmin&&demin[headmin]<l) 66 headmin++; 67 } 68 ans=max(ans,(r-l+1)*(k-i+1)); 69 r++;//枚举下一个右边界 找最大值 70 } 71 } 72 } 73 cout< endl; 74 } 75 }
G.Removing Stones(假博弈 思维?)
•题意
有n堆石头,如果石头总数是奇数的话,就从最小堆中拿走一块
如果总数是偶数堆或者是减少石头后,那每次从不为0的任意两堆中同时拿走一块
如果最后都拿完,则Mark胜利
现在给出每堆石头数,问存在多少对[l,r]使得可以有优胜情况
•思路
可以想一种极端情况,所有的堆都和最大的一堆一起拿
那最大的一堆个数就是除本身以外所有堆的和
通常情况就是其他堆之间也可以一起拿,
那这个问题就转化成了
有多少个区间使得在这个区间里,最大的堆<=其他堆的和
貌似不太好找...但是!有一句话叫做正难则反!
那我们可以用所有的情况-不符合的情况_(:з」∠)_
那怎么找不符合的情况呢?
我们可以通过枚举每一个堆 i,以这个堆为最大堆
利用 最大的堆>其他堆的和,找到极限左边界,
然后枚举左边界,通过固定的左边界L,找到右边界R
那这个左边界L所构成情况就是 L—i,L—i+1...... L—R
也就是每个L都有(R-i+1)种不符合的情况
•代码
View Code1 #include2 using namespace std; 3 typedef long long ll; 4 #define N 300005 5 ll T,n,m; 6 ll a[N]; 7 8 int main() 9 { 10 cin>>T; 11 while(T--) 12 { 13 cin>>n; 14 ll ans=(ll)n*(n+1)/2; 15 for(ll i=1;i<=n;i++) 16 cin>>a[i]; 17 for(ll i=1;i<=n;i++)//枚举堆i 18 { 19 ll l=i,r=i,sum=0; 20 while(l>1&&a[i]>sum+a[l-1]) //对于每一个a[i],找符合条件的最左边界 21 l--,sum+=a[l]; 22 23 for(int L=l;L<=i;L++) //枚举L 24 { 25 while(r sum+a[r+1])//固定L,找到最右边界的 26 r++,sum+=a[r]; 27 28 ans-=(r-i+1);//减去由L与i及其右边组成的 29 sum-=a[L]; 30 } 31 } 32 cout< endl; 33 } 34 }
H.Magic Line(思维)
•题意
给你 n 个点,满足 n 为偶数,并且这 n 个为不重复的整数点;
求将这 n 个点均分为两部分的直线 L,即有 n/2 个点严格在 L 上方,n/2个点严格在 L 下方;
输出 L 上的任意两个点 (x1,y1),(x2,y2) 的坐标;
要 x1,y1,x2,y2 为整数,且 |x1|,|y1|,|x2|,|y2| ≤ 109;
•思路
看到这个题,我第一反应是分蛋糕的题
但是他不是!
然后...然后就game over了
用出题人的话来说就是
假设没有x重复,可以按x排序,然后在中间画一条线
现在有x重复,可以按照(x,y)双关键字排序,在中间偏左和中间偏右的两个中间点之间,画一条稍微稍微倾斜的线
两种实现方式的具体细节在代码中解释
•代码
实现方式一
View Code1 #include2 using namespace std; 3 const int inf=1e8+5; 4 struct node 5 { 6 int x,y; 7 }s[1005]; 8 9 bool cmp(node a,node b) 10 { 11 if(a.x==b.x) 12 return a.y<b.y; 13 return a.x<b.x; 14 } 15 16 int main() 17 { 18 int t; 19 cin>>t; 20 while(t--) 21 { 22 int n; 23 cin>>n; 24 for(int i=1;i<=n;i++) 25 cin>>s[i].x>>s[i].y; 26 sort(s+1,s+1+n,cmp); 27 int x1=s[n/2].x-1; 28 int x2=s[n/2+1].x+1; 29 int y1=s[n/2].y+inf; 30 int y2=s[n/2+1].y-inf; 31 cout< ' '< ' '< ' '< endl; 32 //①为什么要±1 33 //稍微倾斜一下,并且保证不能有交点 34 //x±(0,1],又因为是整数,x±1 35 36 //②为什么是inf 37 //y变化的很大,x变化的很小,这条线及其接近于竖直 38 //才是稍微稍微倾斜一下 39 40 //③为什么是±inf 41 //y±相同的数,使得中间两个点这关于条线对称 42 //正好分成左右两个部分使得左右的点个数相同 43 } 44 }
实现方式二
View Code1 #include2 using namespace std; 3 #define ll long long 4 const int maxn=1e8+5; 5 struct node 6 { 7 int x,y; 8 }s[maxn]; 9 bool cmp(node a,node b) 10 { 11 if(a.x==b.x) 12 return a.y<b.y; 13 return a.x<b.x; 14 } 15 16 int t; 17 int main() 18 { 19 cin>>t; 20 while(t--) 21 { 22 int n; 23 cin>>n; 24 for(int i=1;i<=n;i++) 25 cin>>s[i].x>>s[i].y; 26 sort(s+1,s+n+1,cmp); 27 int xx=s[n/2].x; 28 int yy=s[n/2].y; 29 cout< 1<<' '< ' '< 1<<' '< 1<<endl; 30 //①为什么要±1 31 //稍微倾斜一下,并且保证不能有交点 32 //x±(0,1],又因为是整数,x±1 33 34 //②为什么要是inf 35 //y变化的很大,x变化的很小,这条线及其接近于竖直 36 //才是稍微稍微倾斜一下 37 38 //③为什么是±inf 39 //y±相同的数,使得中间两个点这关于条线对称 40 //正好分成左右两个部分使得左右的点个数相同 41 42 //④为什么y要+1 43 //中间可能有整点,为了防止对称的时候过中间的整点y+1 44 //为什么是+1,因为x2-x1>=2,当x2-x1=2时,k=y2-y1+2/x2-x1=(y2-y1/x2-x1)+1 45 //可能过整点,就可能相交! 46 } 47 }
J.LRU management(模拟 unordered_map+list)
•题意
一个固定长度的LRU数组,每次有两种操作
0号操作:若查询的字符串在数组中存在,就把它取出来,并原有值保持不变,压入数组末尾。
若查询的字符串不存在则直接加入到末尾,值为输入的v。
输出此字符串的值v
1号操作:先得出输入的字符串在数组中的下标k,查询下标为(k+v)的数组元素的值,
若k或(k+v)不存在都输出Invalid。
•思路
用unordered_map
::iterator>来模拟 不会这两个函数的可以先去看下用法....
注意用cout时换行用endl会TLE,改成'\n'就会AC,时间快的还很多
以前都不知道endl这么费时间_(:з」∠)_
•代码
View Code1 #include2 using namespace std; 3 struct Node 4 { 5 string ss; 6 int v; 7 }; 8 list l; 9 unordered_map<string,list ::iterator> mp; 10 11 int main() 12 { 13 std::ios::sync_with_stdio(false),cin.tie(0); 14 int t;; 15 cin>>t; 16 while(t--) 17 { 18 int n,m; 19 cin>>n>>m; 20 int op,v; 21 string s; 22 mp.clear(),l.clear(); 23 for(int i=1;i<=n;i++) 24 { 25 cin>>op>>s>>v; 26 27 ///1操作 28 if(op) 29 { 30 ///存在则得出输入的字符串在数组中的下标k,查询下标为(k+v)的数组元素的值 31 if(mp.count(s)) 32 { 33 list ::iterator it=mp[s]; 34 if(v==-1) 35 { 36 if(it==l.begin()) 37 cout<<"Invalid\n"; 38 else 39 { 40 it--; 41 cout< v<<"\n"; 42 } 43 } 44 else if(v==0) 45 cout< v<<"\n"; 46 else 47 { 48 it++; 49 if(it==l.end()) 50 cout<<"Invalid"<<"\n"; 51 else 52 cout< v<<"\n"; 53 } 54 } 55 ///若k或(k+v)不存在都输出Invalid。 56 else 57 cout<<"Invalid\n"; 58 } 59 ///0操作 60 else 61 { 62 ///存在则 把它取出来,原有值保持不变,压入数组末尾。 63 if(mp.count(s)) 64 { 65 list ::iterator it=mp[s]; 66 cout< v<<"\n"; 67 l.push_back(Node{it->ss,it->v}); 68 l.erase(it); 69 it=l.end(); 70 it--; 71 mp[s]=it; 72 } 73 ///不存在则直接加入到末尾,值为输入的v。 74 else 75 { 76 cout< "\n"; 77 l.push_back(Node{s,v}); 78 list ::iterator it=l.end(); 79 it--; 80 mp[s]=it; 81 } 82 int sz=l.size(); 83 while(sz>m) 84 { 85 mp.erase(l.begin()->ss); 86 l.pop_front(); 87 sz--; 88 } 89 } 90 } 91 } 92 }