问题直接
- 去你大爷的long long问题!!!!!!!!!!!!!!
- 当是26个字母时,优先使用数组[26];
- 像map,set之类的stl都是有一定时间复杂度的,所以能用vector和arr最好用这两个
- 利用前缀和时,因为左边l要-1,所以记住(1)数组要从i=1开始放。(2)l 的初始化是1不是0!
961的B1/B2
题目
题目的大概意思为
给你一个数n,与一个总数m;
再分别给你n个数a1,a2,a3…,an。
要求求一个数,为a数组数的相加,要是ai和aj的数相差不大于一,且总值小于m。求这个数的最大值。
这道题的难点在于,知道a个x,b个y,m,求如何ans=a1x+b1y<=m,求ans最大。
思路
b1有很多种思路,可以用B2的思路,也可以
1.双指针
先将数组进行排序
用一个数j来记录开始,i来记录当前位置,然后,里面的x相加,如果超过了(a[i]-a[j]>1或x>m),就将前面的a[j]位置的数减去,用while循环!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
ll n, m;
cin >> n >> m;
ll a[n+5];
for(int i=1; i<=n; i++) cin>>a[i];
sort(a+1,a+n+1);
ll ans=0,i=0,j=1,x=0;
while(i<=n) {
i++;
x+=a[i];
while(x>m||a[i]-a[j]>1) {
x-=a[j];
j++;
}
ans=max(ans,x);
}
cout<<ans<<endl;
}
2.暴力搜索
因为这道题其实就是求在知到a,b的情况下,求ax+by<m的条件下怎么离m最近,这个时候,我们可以将所以情况遍历,从全是x到全是y一个一个遍历。(丫,我因为long long wa了,哭哭)
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
set<int>st;
map<int,int>mp;
int ans=0;
void find_max(int a,int b) {
int aa=mp[a];
int bb=mp[b];
for(int i=0; i<=aa; i++) {
int now=i*a;
if(now>m)
{
return ;
}
int shen=(m-now)/b;
ans=max(ans,(now+b*min(bb,shen)));
}
}
void solve() {
cin>>n>>m;
int x;
st.clear();
mp.clear();
ans=0;
for(int i=0; i<n; i++) {
cin>>x;
mp[x]++;
st.insert(x);
}
for(auto it=st.begin(); it!=st.end(); it++) {
int num=*it;
int it_num=mp[num];
ans=max(ans,num*min(m/num,it_num));
if(mp[num+1]>0) {
find_max(num,num+1);
}
}
cout<<ans<<"\n";
}
3. 思维
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
ll arr[200005];
map<ll,ll>mp;
ll ans=0;
void find_max(ll a,ll b) {
if(a>m) {
return ;
}
ll aa=mp[a];
ll bb=mp[b];
ll num_a=min(m/a,aa);
ll count_a=num_a*a;
ll num_b=min((m-count_a)/b,bb);
ll count=num_a*a+num_b*b;
ll shen_num=m-count;
ll shen_b=bb-num_b;
ll add=min(min(shen_num,shen_b),num_a);
ans=max(ans,count+add);
}
void solve() {
cin>>n>>m;
mp.clear();
ans=0;
for(int i=0; i<n; i++) {
cin>>arr[i];
}
for(int i=0; i<n; i++) {
cin>>mp[arr[i]];
}
sort(arr,arr+n);
for(int i=0; i<n; i++) {
ans=max(ans,arr[i]*min(mp[arr[i]],m/arr[i]));
if(arr[i+1]-arr[i]==1) {
find_max(arr[i],arr[i+1]);
}
}
cout<<ans<<"\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
cin >> q;
while (q--) {
solve();
}
return 0;
}
962的C
题目
思路
1.预处理
这道题要学会奖字母‘a’-'z’转变为0-26,暴力是不行的,所以要预处理,而预处理的思路就是因为字母才26个!!!!,所以开二维是没问题的,不要傻颠颠的想着,开26个数组,二维数组脑子被吃了嘛!
2. 空间复杂度和时间复杂度的理解
先来个正确的代码
对于这段代码我先后经历过好几种空间复杂度与时间复杂度的错误。
- 开全局变量时,如果n只能在后面局部输入,全局变量不能用n来代替(报错)
- 空间复杂度报错:
上面两行那种会导致空间复杂度的报错,因为这会导致每次都要开2210^5的内存,然后因为t是10 ^5,所以会导致42210 ^510 ^5=16*10 ^10次方比特,除以两个1024为152587mb,超内存了。
所以将数字换为n类型(下面两行那种)就不会导致mle了,但下面那种不能放到全局变量,所以最好还是把solve1()的内容放回solve(),或者改为vector。
这种有三种解决方法:1、改为n+5类型。2、改为全局变量。3、改为vector。 - 时间复杂度报错:
这个代码中,我是因为放在全局变量,想要初始化,所以用了memset,但TLE了。
因为memset为O(n),所以当每次去除10 ^5还要10 ^5倍后,会超时。
要么看后面,1到n都扫过了,直接不初始化,可过。
要么用vector。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
string a,b;
int n,q;
cin>>n>>q;
cin>>a>>b;
vector<array<int,26>>aa(n+2),bb(n+2);
for(int i=0; i<n; i++) {
for(int j=0; j<26; j++) {
aa[i+1][j]=aa[i][j];
bb[i+1][j]=bb[i][j];
if((a[i]-'a')==j) {
aa[i+1][j]++;
}
if((b[i]-'a')==j) {
bb[i+1][j]++;
}
}
}
while(q--) {
int l,r,ans=0;
cin>>l>>r;
for(int i=0; i<26; i++) {
int num_a=aa[r][i]-aa[l-1][i];
int num_b=bb[r][i]-bb[l-1][i];
//cout<<num_a<<" "<<num_b<<"\n";
ans+=abs(num_a-num_b);
}
if(ans%2==0) {
cout<<ans/2<<"\n";
} else {
cout<<(ans/2)+1<<"\n";
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
960的C
题目
思路
这是一道数学思维打表题,找规律。
通过画表能发现,重复两次后,能将所有单个的去掉。此时剩下的数按左上右下的对角线方式相加即可。like:
注意点:
- 开long long
- 如果想要accumulate函数来求和,要在后面的0前面加(long long),这里wal好多次!
#include <bits/stdc++.h>
using namespace std;
//using ll=long long;
#define int long long
void solve() {
int n;
cin>>n;
int ans=0;
vector<int>a(n+1);
vector<int>b(n+1);
for(int i=1; i<=n; i++) {
cin>>a[i];
}
a[0]=0;
int num=0;
for(int k=0; k<2; k++) {
int sum=accumulate(a.begin(),a.end(),(long long)0);//这里要(long long!)
ans+=sum;
vector<int>v(n+1);
int c=0;
for(int i=1; i<=n; i++) {
v[a[i]]++;
if(v[a[i]]==2&&a[i]>c)c=a[i];
b[i]=c;
}
for(int i=1; i<=n; i++) {
a[i]=b[i];
}
num=0;
for(int i=1; i<=n; i++) {
ans+=a[i]*(n-i+1);
}
cout<<ans<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
958 C
思路
- 会求二进制中为1的位置
cin>>n;
for(int i=0;i<=62;i++)
{
if(n>>i&1)
{
cout<<i<<"\n";
//该位置在二进制中为1
}
}
2.思路
题目让我们从小到大,但思路可以反过来从大到小,因为n肯定是最大的,我们就可以在二进制位置的0不断往左移(即让数不断变小)【小红书视频】
#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
void solve() {
int n,num;
cin>>n;
set<int>st;
st.insert(n);
for(int i=0;i<=62;i++)
{
if(n>>i&1)
{
num=n^(1LL<<i);
if(num==0)
{
continue;
}
st.insert(n^(1LL<<i));
}
}
cout<<st.size()<<"\n";
for(auto it=st.begin();it!=st.end();it++)
{
cout<<(*it)<<" ";
}
cout<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
964的F
题目
思路
这道题思路其实并不难,难就难在阶乘那里可能会整数溢出。看了大部分的代码,都是利用快速幂和阶乘的模逆元来完成的
#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
const int N=200005;
const int mod=1000000007;
int fac[N],inv[N];
int ans=0;
//快速幂
int pw(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res;
}
//求阶乘
int qiu(int n, int k) {
if (k > n) return 0;
if(k==0||n==k) {
return 1;
}
return fac[n] * inv[k] % mod * inv[n - k] % mod;
}
void solve() {
int n,k,x,ans=0;
cin>>n>>k;
int mid=k/2+1;
int sum1=0,sum0=0;
for (int i=0; i<n; i++) {
cin>>x;
if(x==1) {
sum1++;
} else {
sum0++;
}
}
mid=max(mid,k-sum0);
while(mid<=sum1&&mid<=k) {
int qiu1=qiu(sum1,mid);
int qiu2=qiu(sum0,k-mid);
//cout<<"mid"<<mid<<" "<<qiu1<<" "<<qiu2<<"\n";
ans+=(qiu1*qiu2)%mod;
mid++;
}
cout<<ans%mod<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
fac[0] = inv[0] = 1;
for (int i = 1; i < N; i++) {
fac[i] = 1ll * fac[i - 1] * i % mod;
//求模逆元
inv[i] = pw(fac[i], mod - 2);
}
while(t--) {
solve();
}
return 0;
}
964的G2
题目
思路
这道题的思路就是学会交互和三分
#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
const int N=200005;
void solve() {
int l=1, r=1000,res;
while(l+2<r) {
int mid1=l+(r-l)/3;
int mid2=l+((r-l)/3)*2;
cout<<"? "<<mid1<<" "<<mid2<<"\n";
cin>>res;
if(res==(mid1*mid2)) {
l=mid2+1;
} else if(res==((mid2+1)*mid1)) {
l=mid1+1;
r=mid2;
} else {
r=mid1;
}
cout.flush();
}
if(l+1==r) {
cout<<"? "<<l<<" "<<r<<"\n";
cin>>res;
if(res==(l+1)*(r+1)) {
cout<<"! "<<l<<"\n";
} else {
cout<<"! "<<r<<"\n";
}
} else {
cout<<"? "<<l<<" "<<l+1<<"\n";
cin>>res;
if(res==(l+1)*(l+2)) {
cout<<"! "<<l<<"\n";
} else if(res==l*(l+1)) {
cout<<"! "<<r<<"\n";
} else {
cout<<"! "<<l+1<<"\n";
}
}
cout.flush();
}
signed main() {
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}
963的c
题目
思路
这道题的思路为周期+差分题目,可以将所有数的聚集在max后的2k中,如果在这2k的期间内不行,那就是不行。
然后思路就是,开灯+1,关灯-1,当isopen==n的时候,就说明灯都打开了。(差分思路)
#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
const int N=200005;
void solve() {
int n,k,maxn=0,isopen=0,num,yu;
cin>>n>>k;
int a[n+2];
int open[2*k+1]={0};
for(int i=0; i<n; i++) {
cin>>a[i];
maxn=max(maxn,a[i]);
}
for(int i=0; i<n; i++) {
num=maxn-a[i];
yu=num%k;
if((num/k)%2==0) {
isopen++;
open[k-yu]--;
open[2*k-yu]++;
} else {
open[k-yu]++;
open[2*k-yu]--;
}
}
for(int i=0;i<=2*k;i++){
// cout<<"open"<<isopen<<" "<<open[i]<<"\n";
isopen+=open[i];
if(isopen==n)
{
cout<<maxn+i<<"\n";
return;
}
}
cout<<-1<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}