zoukankan      html  css  js  c++  java
  • 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
    ",ans,min(one,zero)*2);
    26     return 0;
    27 }

    D:Big Integer

    题意:

    $Aleft(i^j ight)$表示$i^j$个$1$,求$Aleft(i^j ight)equiv0mod p$有多少对 

    • $1leq ileq n$
    • $1leq jleq m$
    • $p$为质数

    思路:

    $1111$可写为$frac{{10}^n-1}9$,则可以转化为

    $$frac{{10}^n-1}9equiv0mod p$$

    $$left({10}^n-1 ight)ast invleft(9 ight)equiv0mod p$$

    若$p=2或5$,明显答案为$0$,

    若$p$与$9$互质,$invleft(9 ight) eq0$,

    所以 $${10}^n-1equiv0mod p$$ 

    即   $${10}^nequiv1mod p$$

    由欧拉定理得$varphileft(p ight)$必定满足,但不一定是最小的。

    所以我们可以求得最小循环节$x$,复杂度$Oleft(sqrt{p}log p ight)$

    转化为有多少对$xmid{i^j}$

    考虑固定$j$

    对$x$进行质因数分解,

    $x=p_1^{ax_1}ast p_2^{ax_2}astldotsast p_k^{ax_k}$

    那么$i$的每种质因子的个数至少有$lceilfrac{ax}j ceil$

    令$g=p_1^{lceilfrac{ax_1}j ceil}ast p_2^{lceilfrac{ax_2}j ceil}astldotsast p_k^{lceilfrac{ax_k}j ceil}$,$gmid i$

    在$n$的范围内有$lceilfrac{n}g ceil$个。

    $g$随着$j$的改变而改变。

    令$mx=maxleft(ax_1,ax_2,ldots\,ax_k ight)$,若$jgeq mx$,对后面的$j$,影响不会改变。有$left(m-j ight)astlceilfrac{n}g ceil$个。

    考虑$p=3$与$9$不互质,根据整除$3$的性质,有$nsetminus3ast 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
    ",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
    ",ans);
    75     }
    76 }

    F:Planting Trees

    题意:

    给出$n imes 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
    ",ans);
    50     }
    51 }

    G:Removing Stones

    题意:

    给出$n$堆石子,每次可以选非空且不同的两堆,然后各取一个石子,此操作可以进行任意次,很明显只要数量为偶数,Mark必定可以完成,现在问你有多少个区间$left(l,r ight)$,Mark可以完成。

    思路:

    很明显题目可以转化为求多少个这样的区间$left(l,r ight)$:

    $$max_{lleq ileq r} leq sum_{i=l}^ra_i$$

    可以用$RMQ$查询最大值,前缀和查询区间和,考虑计数,可以分治。

    每次在小区间枚举一个端点,在大区间二分另一个端点

    比如左边区间小,就在右边二分找另一个端点。

    记最值下标为$k$,则需满足$2ast a[k]leq sum[r]-sum[l-1]$

    枚举左边,所以$sum[r]geq 2ast a[k]+sum[l-1]$,找第一个大于$2ast 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
    ",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
    ",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
    ",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
    ",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
    ",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
    ",prev(p)->second);
    55                     } else if(v==1) {
    56                         if(next(p)==list.end()) ok=false;
    57                         else printf("%lld
    ",next(p)->second);
    58                     } else printf("%lld
    ",p->second);
    59                 }
    60                 if(!ok) puts("Invalid");
    61             }
    62         }
    63     }
    64 }
  • 相关阅读:
    模板集合
    [NOIP2005普及组]循环(高精度+数学)
    KEYENCE Programming Contest 2021
    AtCoder Regular Contest 111
    Educational Codeforces Round 99 (Rated for Div. 2)
    AtCoder Beginner Contest 183翻车记
    上古退役选手康复训练1——CSP2020J-2
    [SNOI2020]取石子(数学+打表找规律)
    selenium爬取拉勾网招聘信息
    scrapy将爬取的数据存入MySQL数据库
  • 原文地址:https://www.cnblogs.com/ACMerszl/p/11253377.html
Copyright © 2011-2022 走看看