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



 

想的太多,做的太少;
原文地址:https://www.cnblogs.com/littlerita/p/12420147.html