zoukankan      html  css  js  c++  java
  • HZNU Training 2 for Zhejiang Provincial Competition 2020

    A - A

     POJ - 3494 

    先回忆一下单调栈:
    解决如下问题:一个点可以向右延伸和向左延伸的最大值,
    维护一个单增的栈,那么对于栈里的元素a来说,右边的元素都能向右延伸的,
    左边的元素都不能延伸,如果说一个要进来的元素破坏了单调性,那么我就一直pop
    最后一个pop的元素实际上就是 这个要入栈的元素能向左延伸的最大长度,那么维护一下
    最后push进去这个元素的向左延伸的最大值,然后队尾push-1,让他们全出栈;

    解法:将每个矩形扩充出来,满足    if(x==1)h[i][j]=h[i-1][j]+1;  else h[i][j]=0;这样就转换成了求最大矩形面积问题;

    那注意push-1使得栈中元素全部出栈即可。

    #include<cstdio>
    #include<cstring>
    #include<stack>
    using namespace std;
    typedef long long ll;
    const int N=2e3+5;
    int h[N][N];
    int main(){
       int n,m;
       while(~scanf("%d %d",&n,&m)){
        memset(h,0,sizeof h);
        for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
        int x;
        scanf("%d",&x);        
        if(x==1)h[i][j]=h[i-1][j]+1;
        else h[i][j]=0;
        }
        h[i][m+1]=-1;
        }
        int ans=0,tmp,t;
        for(int i=1;i<=n;i++){
        stack<int>stk;
        for(int j=1;j<=m+1;j++){
        if(stk.empty()||h[i][j]>=h[i][stk.top()])stk.push(j);
        else {
        while(!stk.empty()&&h[i][j]<h[i][stk.top()]){
        t=stk.top();
        stk.pop();
        tmp=(j-t)*h[i][t];
        if(tmp>ans)ans=tmp;
        }
        stk.push(t);
        h[i][t]=h[i][j];
        }
        }
        }
        printf("%d
    ",ans);
       }
        return 0;
    }
    View Code

    B - B

     CodeForces - 1301C 

    题意:给你一个n,m,一个长度为n的01串,有m个1,问你怎么弄,使得有1的区间数最大;

    解法:这题偏思维,你直接想1的区间不好想,转换为求全为0的区间,

    拿总和-全为0的就是了,问题为n-m个0放到m+1个区间,怎么放;
    先令 cnt1=(n-m)/(m+1),表示每个区间的平均0,
    然后 cnt2=(n-m)%(m+1),表示剩余的0,那剩余的0怎么放呢?
    开始我想放在两边,但这样是不对的,应该继续插在中间的区间放,
    那这样的话 中间区间数为 getsum(cnt1)*(m+1)
    加上剩余的 cnt2*(cnt1+1);
    这算是正难则反的思想的体现吧;主要是没想到分开区间来放。

    #include<cstdio>
    #include<cstring>
    #include<stack>
    using namespace std;
    typedef long long ll;
    const int N=2e3+5;
    ll getsum(ll x){return x*(x+1)/2;}
    int main(){
          int t;
          scanf("%d",&t);
        while(t--){
        ll n,m;
        scanf("%lld %lld",&n,&m);
        ll tot=getsum(n);
        ll cnt1=(n-m)/(m+1);
        ll cnt2=(n-m)%(m+1);
        ll ans=tot-getsum(cnt1)*(m+1)-cnt2*(cnt1+1);
        printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    C - C

     Gym - 102411M 

    题意:一个序列,问你有多少个  a[j]-a[i]=a[k]-a[j]1i<j<kn;

    这题就是转化为 a[k]=2*a[j]-a[i],每次看看  2*a[j]-a[i]在不在, 那问题就是枚举时如何保证   1i<j<kn,

    学长们的解法十分奇妙,就是每次读入时枚举k,然后mp[2*a[k]-a[i]]++ ;这样既保证了   j>i  ,又保证了k>j;

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
    #define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
    #define pb push_back
    #define pf push_front
    #define fi first
    #define se second 11
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ldb;
    typedef double db;
    // const db PI=acos(-1.0);
    const ll INF=0x3f3f3f3f3f3f3f3fLL;
    const int inf=0x3f3f3f3f;//0x7fffffff;
    const double eps=1e-9;
    const ll MOD=9999991;
    const int maxn=2e3+5;
    ll a[maxn];
    int main(){
        int n,t;
        scanf("%d",&t);
        while(t--){
        unordered_map<ll,ll>mp;
        int ans=0;
        scanf("%d",&n);
        for(int k=1;k<=n;k++){
        scanf("%lld",&a[k]);
        ans+=mp[a[k]];
        for(int i=1;i<k;i++)mp[2*a[k]-a[i]]++;
        }
        printf("%d
    ",ans);
        } 
         // system("pause");
        a[j]-a[i]=a[k]-a[j];
        a[k]=2*a[j]-a[i];
        return 0;
    }
    View Code

    F - F

     CodeForces - 1256D 

    题意:给你一个01串,我也不知道为什么出题喜欢01串,一共可以跳k步,使得这个值最小。
    解法:一开始用O(n^2)的算法,但这是过不了的,然后开始优化,也没想出个门道。
     正确的做法是这样的:用一个head 表示能到的最近的1,
    若 k>=i-head,那直接跳过去,让head++,为什么head++是正确的呢?因为是向后枚举的
    不可能head++这个位置是0,是0直接换掉了;
    若 k<=i-head;就跳i-k个位置。会不会i-k那个位置是0呢?
    这可以推出矛盾,如果i-k那个位置为0,那直接跳最近的1不就行了吗,那不就是满足条件1吗;

    #include<bits/stdc++.h>
    #include<cstdio>
    #include<stack>
    using namespace std;
    #define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
    #define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
    #define pb push_back
    #define pf push_front
    #define fi first
    #define se second 11
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ldb;
    typedef double db;
    // const db PI=acos(-1.0);
    const ll INF=0x3f3f3f3f3f3f3f3fLL;
    const int inf=0x3f3f3f3f;//0x7fffffff;
    const double eps=1e-9;
    const ll MOD=9999991;
    const int maxn=1e6+5;
    // ll a[maxn];
    char s[maxn];
    int main(){
        int t;
        scanf("%d",&t);
        while(t--){
         ll n,k;
        scanf("%lld %lld",&n,&k);
        string s;
        cin>>s;
        ll head=0;
        while(s[head]=='0')head++;
        for(ll i=head;i<n;i++){
        if(k<=0)continue;
        if(s[i]=='0'){
        if(k>=i-head)swap(s[i],s[head]),k-=(i-head),head++;
        else if(i-k>=0){
        swap(s[i],s[i-k]);
        k=0;
        }
        
        }
    
        }
        cout<<s<<endl;
        }
       // system("pause");
        return 0;
    }
    View Code

     题意:x+y=a,lcm(x,y)=b;让你求x,y;

    做法:写了一次穷举,然后T;

    正解:

    可能这就是数论的巧妙之处吧;

    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
    #define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
    #define pb push_back
    #define pf push_front
    #define fi first
    #define se second 11
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ldb;
    typedef double db;
    // const db PI=acos(-1.0);
    const ll INF=0x3f3f3f3f3f3f3f3fLL;
    const int inf=0x3f3f3f3f;//0x7fffffff;
    const double eps=1e-9;
    const ll MOD=9999991;
    const int maxn=2e3+5;
    // ll a[maxn];
    ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
    ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
    int main(){
          ll a,b;
          while(~scanf("%lld %lld",&a,&b)){
          ll  c=gcd(a,b);
          ll d=a*a-4*b*c;
          if(d<0){
            printf("No Solution
    ");
          continue;
          }
          ll i=(a-sqrt(d))/2/c;
          ll j=a/c-i;
          ll x=c*i,y=c*j;
          if(x*y/c==b)printf("%lld %lld
    ",x,y);
         else    printf("No Solution
    ");
    
          }
        return 0;
    }
    View Code



     

    想的太多,做的太少;
  • 相关阅读:
    SPOJ 1812 LCS2 后缀自动机
    [APIO2014]回文串 后缀自动机_Manancher_倍增
    SPOJ8222 NSUBSTR
    [HAOI2016]找相同字符 广义后缀自动机_统计出现次数
    洛谷 P3804 【模板】后缀自动机 统计单词出现次数
    洛谷 P1368 工艺 后缀自动机 求最小表示
    力扣题目汇总(反转字符串中的单词,EXCEL表列序号,旋置矩阵)
    力扣题目汇总(重复N次元素,反转字符串,斐波那契数)
    力扣题目汇总(机器人返回原点,按奇偶排序,数字的补数)
    博客园美化的第二天(动态设置,以及结合ps制作)
  • 原文地址:https://www.cnblogs.com/littlerita/p/12420147.html
Copyright © 2011-2022 走看看