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,0看作-1,这一步是很经典的,有很多题都运用到了这种技巧。
- 接下来“区间和为0”转化为“前缀和相等”,也是很经典的的技巧。
- 最后改变思考的角度,不从 (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;
}