每日一题~div3 962 D+E+F(枚举算时间复杂度+经典操作+二分答案)

D题:
给定两个整数 n , x。寻找正整数三元组(a,b,c),不同的顺序算不同的答案。
输出 三元组的个数。
在这里插入图片描述
n x 的范围在 1e6之内。
首先思考暴力的时间复杂度。
枚举 a ,之后枚举 b .c 的个数可以直接计算出来。
因为a*b<=n,所以枚举a b 的时间复杂度不会很大。准确的分析一下,当 a 为1 时,b 的枚举个数为 n ,当 a 为 2 时,b 的枚举个数为 n/2.以此类推为 n+n/2+n/3+… 调和级数 时间复杂度为 nlogn。
反思:当时没有认真的计算复杂度,只是单纯的感觉枚举 a b 的复杂度为 1e3 *1e3 。遂未做。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
void solve()
{
    int sum=0;
    int n,x;cin>>n>>x;
    for (int a=1;a<=min(x,n);a++)
    {
        for (int b=1;a+b<=x&&a*b<=n;b++){
            sum+=min(x-a-b,(n-a*b)/(a+b));
        }
    }
    cout<<sum<<"\n";
}
signed  main()
{
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int t; t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

E题:
在这里插入图片描述
不要忘记取模!!
经典的技巧:

  1. 改写整个序列,把1看作1,0看作-1,这一步是很经典的,有很多题都运用到了这种技巧。
  2. 接下来“区间和为0”转化为“前缀和相等”,也是很经典的的技巧。
  3. 最后改变思考的角度,不从 (l, r) 而从 (x, y) 考虑,统计一个 (x, y) 会被几个 (l, r) 包含,这一步也很经典。

至于具体的计算,个人还是喜欢数学公式的推导。感觉这样会更清楚。如下:
在这里插入图片描述

其实这样思考不是很全面。因为实际上合法的区间 除了通过 两个前缀和相等得来的。还有直接前缀和为零的区间。
至于为什么程序还是对的,想了想不是很明白。在这里插个眼。有时间再想想。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int p=1e9+7;
void solve()
{
    string s;cin>>s;int n=s.size();
    vector<int>a(n+1);
    for (int i=1;i<=s.size();i++)
    {
        int t=s[i-1]=='1'?1:-1;
        a[i]=a[i-1]+t;
    }
  
   int sum=0;
map<int,int>mp;
//枚举的区间 x 
  for (int i=n;i>=1;i--)
  {
  
      if(mp.count(a[i-1])!=0)
       	  sum=(sum+i*(mp[a[i-1]])%p)%p;
       mp[a[i]]=(mp[a[i]]+n-i+1)%p;
    
   
  }
 cout<<sum<<"\n";


}
signed  main()
{
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int t; t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

F题
在这里插入图片描述
意识不到二分。big problem
可以二分 数值。数列中大于等于这个数的都要进行操作。check 操作次数是否>=k。
如果 Mid 满足,那么l=mid+1,扩大mid ,减少操作次数
如果最终 结果的 操作次数大于k, 那么减去多于的数值。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
int n,k;
vector<int>a;vector<int>b;
int check(int mid)
{    int cnt=0;
    for (int i=0;i<n;i++)
    {
        if (a[i]>=mid)
      {  int d=a[i]-mid;
        cnt+=1+(d/b[i]);
      }
    }
    return cnt>=k;
}
void solve()
{
    cin>>n>>k;
    a.resize(n);b.resize(n);
    for (int i=0;i<n;i++)
    cin>>a[i];
    for (int i=0;i<n;i++)
    cin>>b[i];

    //二分 取的数的最小值. 大于等于这个数的都操作
    int l=1,r=1e10;
    while(l<=r)
    {
        int mid=l+r>>1;
        if (check(mid)) l=mid+1;
        else r=mid-1;
    }
    int maxn=l-1; 
    int sum=0;int cnt=0;
    for (int i=0;i<n;i++)
    {
        if (a[i]>=maxn)
        {
            int k=(a[i]-maxn)/b[i];
             cnt+=k+1;
            sum+=(a[i]+a[i]-b[i]*k)*(k+1)/2;
           // cout<<sum<<"\n";
        }
    }
    if(cnt>k)sum-=(cnt-k)*maxn;
    cout<<sum<<"\n";
    
}
signed  main()
{
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int t;// t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值