2019牛客多校第三场
B.Crazy Binary String
传送:https://ac.nowcoder.com/acm/contest/883/B
题意:有一个长度为$n$的‘0’’1‘字符串,询问’0‘,’1‘个数相同的最长的子串和子序列为多长。
数据范围:$1<=n<=10^5$。
分析:首先考虑子序列,那么就是$ans2=min(num[0],num[1])*2$。
将0变为-1,做前缀和。找前缀和相等的跨越长度最大的即可。同时需要特别0位置设为0。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+10; 4 int num[maxn]; 5 map<int,int> mp; 6 vector<int> g[2*maxn]; 7 int main(){ 8 string s; 9 int n;scanf("%d",&n); 10 cin >> s; 11 int kk=0; 12 for (int i=0;i<n;i++){ 13 if (s[i]=='0'){ 14 num[i+1]=-1; 15 kk++; 16 } 17 else num[i+1]=1; 18 } 19 kk=min(kk,n-kk); 20 int ans2=kk*2; 21 num[0]=0; 22 for (int i=2;i<=n;i++) num[i]=num[i]+num[i-1]; 23 int tot=0; 24 for (int i=0;i<=n;i++){ 25 if (mp[num[i]]==0){ 26 mp[num[i]]=++tot; 27 g[tot].push_back(i); 28 } 29 else g[mp[num[i]]].push_back(i); 30 } 31 int ans1=0; 32 for (int i=1;i<=tot;i++){ 33 if (g[i].size()>=2){ 34 int tmp=g[i][g[i].size()-1]-g[i][0]; 35 ans1=max(ans1,tmp); 36 } 37 } 38 printf("%d %d\n",ans1,ans2); 39 return 0; 40 }
F.Planting Trees
传送:https://ac.nowcoder.com/acm/contest/883/F
题意:有一个$n*n$大小的矩阵,求解一个最大的子矩阵,要求这个子矩阵内任意两个数的差的绝对值$<=m$。
数据范围:$1<=n<=500,a_{ij}<=10^5$。
分析:读完题觉得需要维护子矩阵的最大最小值。写了二维线段树,然后枚举起始行和终止行,再尺取法找列。(然而,线段树复杂度太大,tle。
正解是:枚举起始行和终止行,再枚举右边界去找到可行的最小的左边界。
在枚举右边界的同时,维护两个单调队列,维护最大值和最小值,
最小值的单调队列应该为:下标递增,大小递增;最大值的单调队列应该为:下标递增,大小递解。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF=2333333; 4 const int maxn=510; 5 int a[maxn][maxn],mi[maxn],mx[maxn],q1[maxn],q2[maxn]; 6 int main(){ 7 int t,n,m;scanf("%d",&t); 8 while (t--){ 9 scanf("%d%d",&n,&m); 10 for (int i=1;i<=n;i++) 11 for (int j=1;j<=n;j++) scanf("%d",&a[i][j]); 12 int ans=0; 13 for (int i=1;i<=n;i++){ 14 for (int j=1;j<=n;j++) mi[j]=INF,mx[j]=-INF; 15 for (int j=i;j<=n;j++){ 16 for (int k=1;k<=n;k++){ 17 mi[k]=min(mi[k],a[j][k]); 18 mx[k]=max(mx[k],a[j][k]); 19 } 20 int l1=1,r1=0,l2=1,r2=0; 21 for (int l=1,r=1;r<=n;r++){ 22 while (l1<=r1 && mx[r]>=mx[q1[r1]]) r1--; 23 q1[++r1]=r; 24 while (l2<=r2 && mi[r]<=mi[q2[r2]]) r2--; 25 q2[++r2]=r; 26 while (l<=r && mx[q1[l1]]-mi[q2[l2]]>m){ 27 while (l1<=r1 && q1[l1]<=l) l1++; 28 while (l2<=r2 && q2[l2]<=l) l2++; 29 l++; 30 } 31 ans=max(ans,(j-i+1)*(r-l+1)); 32 } 33 } 34 } 35 printf("%d\n",ans); 36 } 37 return 0; 38 }
另,对面顾老师二维ST表也过了,%%%%%,tttttttql。顾老师代码:
1 #include<bits/stdc++.h> 2 #define rep(i,x,y) for(int i=(x);i<=(y);++i) 3 #define dep(i,x,y) for(int i=(x);i>=(y);--i) 4 #define pb push_back 5 #define fr first 6 #define sc second 7 using namespace std; 8 typedef long long ll; 9 const int N=510; 10 int mx[9][9][N][N],mi[9][9][N][N],a[N][N],rd[N*2]; 11 struct fastio{ 12 static const int s=1<<24; 13 int p,l; 14 fastio(){p=l=0;} 15 inline char gc(){ 16 static char bf[s]; 17 if(p==l)p=0,l=fread(bf,1,s,stdin); 18 return p==l?-1:bf[p++]; 19 } 20 inline bool read(int&x){ 21 char c=gc(); 22 while((c<'0'||c>'9')&&~c)c=gc(); 23 if(c==-1)return 0;x=0; 24 for(;c>='0'&&c<='9';c=gc())x=x*10+c-'0'; 25 return 1; 26 } 27 }io; 28 int cal(int lx,int ly,int rx,int ry){ 29 int tx=rd[rx-lx+1],ty=rd[ry-ly+1]; 30 int zx=rx-(1<<tx)+1; 31 if(ly==ry)return max(mx[tx][0][lx][ly],mx[tx][0][zx][ly])- 32 min(mi[tx][0][lx][ly],mi[tx][0][zx][ly]); 33 int zy=ry-(1<<ty)+1; 34 return max(max(mx[tx][ty][lx][ly],mx[tx][ty][zx][ly]), 35 max(mx[tx][ty][lx][zy],mx[tx][ty][zx][zy]))- 36 min(min(mi[tx][ty][lx][ly],mi[tx][ty][zx][ly]), 37 min(mi[tx][ty][lx][zy],mi[tx][ty][zx][zy])); 38 } 39 int tot; 40 void sol(){ 41 int n,m,ans=0; 42 io.read(n);io.read(m);int t=rd[n]; 43 rep(i,1,n)rep(j,1,n)io.read(a[i][j]); 44 rep(i,1,n)rep(j,1,n)mi[0][0][i][j]=mx[0][0][i][j]=a[i][j]; 45 rep(i,0,t-1)rep(j,1,n)rep(k,1,n){int r=j+(1<<i); 46 if(r>n){ 47 mi[i+1][0][j][k]=mi[i][0][j][k]; 48 mx[i+1][0][j][k]=mx[i][0][j][k]; 49 }else{ 50 mi[i+1][0][j][k]=min(mi[i][0][j][k],mi[i][0][r][k]); 51 mx[i+1][0][j][k]=max(mx[i][0][j][k],mx[i][0][r][k]); 52 } 53 } 54 rep(i,0,t)rep(j,0,t-1)rep(k,1,n)rep(l,1,n){int r=l+(1<<j); 55 if(r>n){ 56 mi[i][j+1][k][l]=mi[i][j][k][l]; 57 mx[i][j+1][k][l]=mx[i][j][k][l]; 58 }else{ 59 mi[i][j+1][k][l]=min(mi[i][j][k][l],mi[i][j][k][r]); 60 mx[i][j+1][k][l]=max(mx[i][j][k][l],mx[i][j][k][r]); 61 } 62 } 63 rep(i,1,n)rep(j,1,n){ 64 int x=j,mi1=a[i][j],mx1=a[i][j]; 65 while(x<=n&&mx1-mi1<=m){ 66 ++x;mi1=min(mi1,a[i][x]);mx1=max(mx1,a[i][x]); 67 } 68 ans=max(ans,x-j);--x; 69 rep(k,i+1,n){ 70 while(x>=j&&cal(i,j,k,x)>m)--x; 71 if((n-i+1)*(x-j+1)<=ans)break;ans=max(ans,(k-i+1)*(x-j+1)); 72 } 73 }printf("%d\n",ans); 74 } 75 int main(){ 76 rep(i,0,8)rep(j,(1<<i)+1,2<<i)rd[j]=i; 77 int t;io.read(t); 78 rep(i,1,t)sol(); 79 }
H.Magic Line
传送:https://ac.nowcoder.com/acm/contest/883/H
题意:给定平面上的$n$个点,确定一条直线将其划分为数量相等的两部分。
数据范围:$1<=T<=10^5,1<=n<=1000,|x_i,y_i|<=1000$。
分析:移动坐标轴,将所有点放在第一象限,然后根据与x轴的斜率排序,斜率相同根据距离排序。
答案直线一定经过中间两点的中间。
然后在第三象限取一个较远的点,然后另一个点就是中间两点的中位点。(再保证答案为整数即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1005; 4 typedef long long ll; 5 struct node{ll x,y,s;} p[maxn]; 6 bool cmp(node a,node b) 7 { 8 ll xx=a.x*b.y-b.x*a.y; 9 return (xx>0 || ((xx==0)&&a.s<b.s)); 10 } 11 int main() 12 { 13 int t;scanf("%d",&t); 14 while (t--) 15 { 16 int n;scanf("%d",&n); 17 for (int i=0;i<n;i++) 18 { 19 ll xx,yy; 20 scanf("%lld%lld",&xx,&yy); 21 p[i].x=1ll*3000+xx; 22 p[i].y=1ll*50000+yy; 23 p[i].s=xx*xx+yy*yy; 24 } 25 sort(p,p+n,cmp); 26 node A=p[n/2-1],B=p[n/2]; 27 A.x=A.x*2-3000;B.x=B.x*2-3000; 28 A.y=A.y*2-50000;B.y=B.y*2-50000; 29 printf("-3000 -50000 %lld %lld\n",(A.x+B.x)/2,(A.y+B.y)/2); 30 } 31 return 0; 32 }
J.LRU management
传送:https://ac.nowcoder.com/acm/contest/883/J
题意:定义LRU算法如下:
操作0:将名字为$s$,数据为$v$的元素放入列表内。如果存在列表内就将原来的取出添加至最后一个位置,否则直接添加到最后。输出$v$。
操作1:查找名字为$s$的元素的位置$k$,并输出$k+xx$位置的数据$v$。
同时列表只能容纳$m$个元素,如果超出,则弹出最早的元素。
数据范围:$1<=q,m<=5e5$
分析:list+map模拟。
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct node{ 4 string s,v; 5 node(){} 6 node(string _s,string _v){ 7 s=_s; v=_v; 8 } 9 }; 10 int m,q; 11 list<node> lt; 12 unordered_map<string,list<node>::iterator> mp; 13 void solve(string s){ 14 char v[20];scanf("%s",v); 15 if (mp.find(s)!=mp.end()){ 16 auto it=mp[s]; 17 strcpy(v,it->v.data()); 18 lt.erase(it); 19 } 20 node tmp; tmp.s=s; tmp.v=v; 21 lt.emplace_back(tmp); 22 mp[s]=prev(lt.end()); 23 printf("%s\n",v); 24 if (lt.size()>m){ 25 mp.erase(lt.begin()->s); 26 lt.pop_front(); 27 } 28 } 29 void solve2(string s){ 30 int v;scanf("%d",&v); 31 if (mp.find(s)==mp.end()){ 32 printf("Invalid\n"); 33 return ; 34 } 35 auto it=mp[s]; 36 if (it==lt.begin() && v==-1 || next(it)==lt.end() && v==1){ 37 printf("Invalid\n"); 38 return ; 39 } 40 if (v==1) it=next(it); 41 else if (v==-1) it=prev(it); 42 printf("%s\n",it->v.data()); 43 } 44 int main(){ 45 int t,op;scanf("%d",&t); 46 char s[30]; 47 while (t--){ 48 lt.clear();mp.clear(); 49 scanf("%d%d",&q,&m); 50 while (q--){ 51 scanf("%d",&op); 52 scanf("%s",&s); 53 if (op==0) solve(s); 54 else solve2(s); 55 } 56 } 57 return 0; 58 }
注:emplace_back优于push_back。用构造函数会比先赋值好再放入费时。