暑假集训日记——7.4(codeforces)

B. Email from Polycarp
题解:
水题,练习一下STL,方便很多

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
int a[26];
int vis[26];
int n,m;
string str,stl;

int main()
{
   ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
   int x,y,z,k,sum=0;
   cin>>n;
   while(n--)
   {
       cin>>str;
       cin>>stl;
       vector<pair<char,int> >s1,s2;
       int flag=0;
       int len=str.length();
       int len1=stl.length();
       for(int i=0;i<len;i++)
       {
           if(s1.empty()||s1.back().first!=str[i])
            s1.push_back({str[i],0});
           s1.back().second++;

       }
       for(int i=0;i<len1;i++)
       {
           if(s2.empty()||s2.back().first!=stl[i])
            s2.push_back({stl[i],0});
           s2.back().second++;
       }
       if(s1.size()!=s2.size())
        cout<<"NO"<<endl;
       else
       {
           for(int i=0;i<s1.size();i++)
           {
               if(s1[i].first!=s2[i].first)
               {
                   cout<<"NO"<<endl;
                   flag=1;
                   break;
               }
               if(s1[i].second>s2[i].second)
               {
                   cout<<"NO"<<endl;
                   flag=1;
                   break;
               }
           }
           if(flag==0)
            cout<<"YES"<<endl;
       }
   }

}

C2. Exam in BerSU (hard version)
题意:
给你n个数字,从左往右有序,问对于每个数字来说,从左往右(到这个数字)取一些数字,如果取得数字一定要包括这个数字,且这些数字的累加和不能超过m,求对于每个数字来说,它最少落下几个数字没取

WA代码:
C 1 C_1 C1能过, C 2 C_2 C2数据太大超时了。两个 s o r t sort sort重复排序太耗时

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
int n,m;

struct note
{
    int x,id;
}s[N];

bool cmp(note a,note b)
{
    return a.x<b.x;
}
bool cmp1(note a,note b)
{
    return a.id<b.id;
}

int main()
{
   ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
   int x,y,z,k,sum=0,ans;
   cin>>n>>m;

   for(int i=1;i<=n;i++)
   {
       cin>>s[i].x;
       s[i].id=i;
       a[i]=a[i-1]+s[i].x;
   }
   for(int i=1;i<=n;i++)
   {
       if(m>=a[i])
        cout<<0<<" ";
       else
       {
           sort(s+1,s+i,cmp);
           sum=0,ans=0;
           for(int j=i-1;j>=1;j--)
           {
               sum+=s[j].x;
               ans++;
               if(a[i]-sum<=m)
               {
                   cout<<ans<<" ";
                   break;
               }
           }
           if(i!=n)
           sort(s+1,s+i,cmp1);
       }
      // cout<<i<<" ";
   }
}

AC代码:版本一
因为数组中的数的范围为1-100所以可以用桶排序,节省排序花费的时间

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];//桶排序
int n,m;

int main()
{
   ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
   int x,y,z,k,sum=0,ans,need,cnt;
   cin>>n>>m;

   while(n--)
   {
      cin>>x;
      cnt=0;
      need=sum-m+x;
      for(int i=100;i>=1&&need>0;i--)
      {
          ans=i*a[i];
          if(ans>=need)
          {
              cnt+=(need+i-1)/i;//需要删除几个数字
              break;
          }
          need-=ans;
          cnt+=a[i];
      }
      sum+=x;//前缀和
      a[x]++;
      cout<<cnt<<" ";
   }
}

AC代码:版本二
因为是要找到区间的从大到小的值,而且含有重复元素,想到可以使用multset
来自动排序,练习一下STL

根据题意理解,前缀和会越来越大,所以越往后重复删除的元素越多,所以从一开始就把以后一定会删除的元素删去,减少时间复杂度
所以优化就是,把次前缀和都删到刚好小于m,然后记录删除的个数。

证明为什么此时删掉的数以后一定会删掉:
1.如果后来进入容器的数字小于删掉的数字,根据从大到小删的原可得;
2.如果后来进入容器的数字不小于删掉的数字,那即使应该先删掉那个后来的数,但由于此时的前缀和仍大于m所以可得;
所以可以先删除,而不用考虑排序后再从大到小删除。

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<functional>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
multiset<int,greater<int> >cup;
int n,m;

int main()
{
   ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
   int x,y,z,k,sum=0,ans,need,cnt,exc=0;
   cin>>n>>m;

   while(n--)
   {
      cin>>x;
      cnt=0;
      need=sum-m+x;
      auto it=cup.begin();
      while(need>0)
      {
          need-=*it;
          it++;
          cnt++;
      }
      sum+=x;
      cup.insert(x);
      cout<<cnt+exc<<" ";

      while(sum>m)//优化,如果不优化还是会超时
      {
          sum-=*(cup.begin());
          exc++;
          cup.erase(cup.begin());
      }
   }
}

D. Extra Element
题解:
把全部的公差存到数组里,然后每个数删一遍,判断是否有唯一的工差,就可求应该删去的数字下标了,练习运用STL

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<functional>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair

const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;

int n,m;
pii a[N];
map<int ,int>maps;

void add(int x,int y)
{
    maps[x]+=y;
    if(maps[x]==0)
        maps.erase(x);
}

int main()
{
   ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
   int x,y,z,k,sum=0,ans,need,cnt,exc=0;
   cin>>n;
   for(int i=0;i<n;i++)
   {
       cin>>a[i].first;
       a[i].second=i;
   }
   sort(a,a+n);
   for(int i=1;i<n;i++)
   {
       add(a[i].first-a[i-1].first,1);
   }
   for(int i=0;i<n;i++)
   {
       if(i==0)
           add(a[i+1].first-a[i].first,-1);
       else if(i==n-1)
           add(a[i].first-a[i-1].first,-1);
       else
       {
           add(a[i].first-a[i-1].first,-1);
           add(a[i+1].first-a[i].first,-1);
           add(a[i+1].first-a[i-1].first,1);
       }
       if((int)maps.size()<=1)
       {
           cout<<a[i].second+1<<endl;
           return 0;
       }
       if(i==0)
           add(a[i+1].first-a[i].first,1);
       else if(i==n-1)
           add(a[i].first-a[i-1].first,1);
       else
       {
           add(a[i].first-a[i-1].first,1);
           add(a[i+1].first-a[i].first,1);
           add(a[i+1].first-a[i-1].first,-1);
       }
   }
   cout<<-1<<endl;

}

让我想起了一道之前做过的月赛题

/* 
Description 
这天小g遇到了一个队列,小g觉得队列乱糟糟的不好看。于是小g希望将队列调整成为一个等差数列 
(差可为0)。但是小g对每个数都最多调整一次,每次可以对这个数加一、减一。请你帮助小g解决这 
个问题,如果能调整出等差队列,输出需要调整的数的最小数量,否则输出-1。 
Input 
第一行一个整数n(2 <= n <= 100000),表示数列中数的个数; 
第二行为n个整数pi (1 <= pi <= 1e9)。 
Output 
输出一个整数,表示操作数量的最小值。如果不存在则输出-1。 
*/
  
#include<bits/stdc++.h> 
using namespace std; 
const int MAX_N=3e5+9; 
const int INF=1e9+9; 
int vec[MAX_N]; 
int res[MAX_N]; 
int N,M,T,S; 
queue<int> que; 
  
int main(){ 
    //freopen("in.txt","r",stdin); 
    //freopen("out.txt","w",stdout); 
    while(cin>>N) 
    { 
        for(int i=0;i<N;i++) 
        { 
            scanf("%d",&vec[i]); 
        } 
        if(N==1 ||N==2) 
        { 
            cout<<0<<endl; 
            continue; 
        } 
        int ax=INF; 
        for(int a=0;a<3;a++) 
        { 
            for(int b=0;b<3;b++) 
            { 
                int t=vec[1]+b-1-(vec[0]+a-1);//全排列,公差 
                int pre=vec[1]+b-1;//第一个数 
                bool f=true; 
                int ans=abs(a-1)+abs(b-1);//改变次数 
                //cout<<pre<<"..."<<t<<"...."<<ans<<endl; 
                for(int i=2;i<N;i++) 
                { 
                    int temp=vec[i]-pre;//每项的公差 
                    if(abs(temp-t)>1)//如果不相等且差值大于一就终止 
                    { 
                        //cout<<vec[i]<<".!!!!..."<<pre<<endl; 
                        f=false; 
                        break; 
                    } 
                    ans+=abs(pre+t-vec[i]);//改变次数 
                    pre=pre+t; 
                } 
                if(f) ax=min(ax,ans); 
            } 
        } 
        if(ax!=INF)cout<<ax<<endl; 
        else cout<<-1<<endl; 
    } 
} 

E. Polycarp and Snakes
题解:
模拟就行了,记住,Polycarp是按字母顺序画蛇的。
首先,我们应该找出每个字母的左上角和右下角出现的最多的地方。

其次,我们应该通过这些字母从“z”到“a”。我们先跳过未找到的字母。如果任何字母的长度和宽度都大于1,就没有办法画蛇。否则,我们应该检查行中的所有元素是否等于当前字母或’’。如果是这样,让我们用“”把这条线划掉,然后继续看下一个字母。如果不是这样,就没有办法画蛇。

如果有答案,对于每条蛇,我们可以输出相关字母的最左上角和最右下角的坐标。如果某个字母没有出现,我们可以假设下一个字母完全覆盖了当前的字母。我们完全可以解决这个问题,通过不超过1+26=27个领域走。

代码太长不想写了…

B. Split a Number
模拟题,不过写起来好麻烦…
以下代码为转载:

#pragma GCC optimize(2) 
#include <iostream>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
//typedef __int128 lll;
ll n,m,k;
ll ans;
string s,t,w,x,y;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    cin>>s;
    n=s.size();
    reverse(s.begin(),s.end());//便于计算数据相加
   //x和y分别是左半端和右半端的最优解
    for(int i=n/2;i>=0;i--){
        x="";
        t=s.substr(0,i);
        w=s.substr(i,n-i);
        if(w[w.size()-1]=='0'||t[t.size()-1]=='0'||t.empty()||w.empty())continue;//跳过前导0和空串
        x=w;
        for(int i=0;i<t.size();i++){//高精加法
            x[i]+=t[i]-'0';
        }
        for(int i=0;i<w.size()-1;i++){
            while(x[i]>'9')x[i]-=10,x[i+1]++;//进位
        }
        if(x[x.size()-1]>'9')x[x.size()-1]-=10,x+='1';//进位
        //cout<<t<<" "<<w<<" "<<x<<"\n";
        break;//因为找到一个解以后,它就是最优解,所以直接break
    }
    for(int i=(n+1)/2;i<n;i++){
        y="";
        t=s.substr(i,n-i);
        w=s.substr(0,i);
        if(w[w.size()-1]=='0'||t[t.size()-1]=='0'||t.empty()||w.empty())continue;
        y=w;
        for(int i=0;i<t.size();i++){
            y[i]+=t[i]-'0';
        }
        for(int i=0;i<w.size()-1;i++){
            while(y[i]>'9')y[i]-=10,y[i+1]++;
        }
        if(y[y.size()-1]>'9')y[y.size()-1]-=10,y+='1';
        //cout<<t<<" "<<w<<" "<<y<<"\n";
        break;
    }
   //比较
    reverse(x.begin(),x.end());//重置数组
    reverse(y.begin(),y.end());
    if(x.empty()){
        cout<<y;
        return 0;
    }
    if(y.empty()){
        cout<<x;
        return 0;
    }
    if(x.size()!=y.size()){cout<<(x.size()<y.size()?x:y);return 0;}
    else{
        for(int i=0;i<x.size();i++){
            if(x[i]!=y[i]){
                cout<<(x[i]<y[i]?x:y);
                return 0;
            }

        }//cout<<x<<" "<<y<<"\n";
        cout<<x;
    }
    return 0;
}

题意:有一个三岁的小屁孩特别喜欢三个数组成的等差数列,他现在有一组数,特别想知道里面含多少由三个数组成的等比数列,他年纪太小不会算,想难为一下搞ACM的。注意 可以不连续,但是等比数列在数组中的位置必须是递增的。
思路:遍历数组,对于每一个值都把他当做等比数列的中间的值x,对K求余,余数为零时,看(x/k)在该数之前出现了多少次,(xk)在该数之后出现了多少次,求和即可。
现在问题转换成,在x之前 x/k,x
k 出现了多少次?
用map数组处理一下左右的数出现了多少次,在遍历数组的时候,每到一个数mapR[x]–;处理完之后mapL[x]++;

#include <set>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <math.h>
#include <string>
#include <vector>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <functional>
#define mem(a) memset(a,0,sizeof(a));
#define mem_1(a) memset(a,-1,sizeof(a));
#define sf(a) scanf("%d",&a)
#define sff(a,b) scanf("%d%d",&a,&b)
#define sfff(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define lson l,mid,i<<1
#define rson mid+1,r,i<<1|1
#define LL long long
const int INF = 0x7FFFFFFF;
const int MAXN = 1010000;
const double PI = acos(-1.0);
const double esp = 1e-10;
using namespace std;
map<LL,LL> L;
map<LL,LL> R;
LL data[MAXN];
int main()
{
    LL n,k;
    scanf("%I64d %I64d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%I64d",&data[i]);
        R[data[i]]++;
    }
    LL ans = 0;
    for(int i=1;i<=n;i++)
    {
        R[data[i]]--;
        if(data[i]%k==0)
        {
            ans += L[data[i]/k] * R[data[i]*k];
        }
        L[data[i]]++;
    }
    printf("%I64d\n",ans);
    return 0;
}

佛系刷题…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值