A - Legs
题意
鸡兔同笼 告诉你有多少条腿 问你最少有多少只动物
思路
就全放兔子四条腿就行
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void solve()
{
int n;
scanf("%d",&n);
int ans=n/4;
if(n%4!=0)
{
ans++;
}
printf("%d\n",ans);
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
B - Scale
题意
给一个 n ∗ n n*n n∗n大小的01矩阵 其中每k*k都是一样的元素 输出缩小k倍的结果
复盘
先开数组弄的结果Re4了 关键是今天In Queue特别慢 就这re一发血亏50分钟 要不然能涨个1000名 气死了
思路
依据题意模拟即可
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void solve()
{
int n,k;
scanf("%d%d",&n,&k);
string s[n+1];
for(int i=1;i<=n;i++)
{
cin>>s[i];
s[i]=" "+s[i];
}
for(int i=1;i<=n/k;i++)
{
for(int j=1;j<=n/k;j++)
{
printf("%c",s[i*k][j*k]);
}
printf("\n");
}
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
C - Sort
题意
给两个字符串 每次给一个区间 问这两个区间至少要修改多少个元素才能保证所有元素个数都相同
复盘
倒是很快就想到是前缀和了 但是一开始没有分字符种类 写出了个这个东西 后来改对的版本又因为我习惯把字符的数组直接开200又MLE1了 因为超长的queue又浪费好多时间
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int N=200010;
int n,q;
string a,b;
int sum[N];
int sta[200],stb[200];
void solve()
{
scanf("%d%d",&n,&q);
cin>>a>>b;
a=" "+a;
b=" "+b;
memset(sta,0,sizeof(sta));
memset(stb,0,sizeof(stb));
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
sta[a[i]]++;
stb[b[i]]++;
for(int j='a';j<='z';j++)
{
sum[i]+=min(sta[j],stb[j]);
}
printf("%d ",sum[i]);
}
printf("\n");
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",(r-l+1)-(sum[r]-sum[l-1]));
}
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
思路
前缀和 求从1的i每个字符都出现了多少次 然后每次全求一遍就行了
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int N=200010;
int sta[N][28],stb[N][28];
void solve()
{
ll n,q;
string a,b;
cin>>n>>q;
cin>>a>>b;
a=" "+a;
b=" "+b;
for(int i=1;i<=n;i++)
{
for(int j='a';j<='z';j++)
{
sta[i][j-'a']=sta[i-1][j-'a'];
stb[i][j-'a']=stb[i-1][j-'a'];
}
sta[i][a[i]-'a']++;
stb[i][b[i]-'a']++;
}
ll sum;
while(q--)
{
int l,r;
cin>>l>>r;
sum=0;
for(int i='a';i<='z';i++)
{
sum+=abs(stb[r][i-'a']-stb[l-1][i-'a']-sta[r][i-'a']+sta[l-1][i-'a']);
}
printf("%lld\n",sum/2);
}
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
D - Fun
题意
不考虑顺序 问有多少组 ( a , b , c ) (a,b,c) (a,b,c)满足 a b + a c + b c ≤ n ab + ac + bc \le n ab+ac+bc≤n且 a + b + c ≤ x a + b + c \le x a+b+c≤x
思路
看一看数据范围 明确给出n和x小于1e6 甚至告诉你所有case加起来小于1e6 如果能直接用公式推出来那肯定是会给1e18的 这个数据明摆着说明这道题应该就是暴力求解 但是需要用一些技巧优化
我们先写出一个最简单的暴力 当然我们知道b和c一开始就是有范围的 对于b有
b
<
x
−
a
,
a
b
+
a
+
b
≤
n
b<x-a,ab+a+b \le n
b<x−a,ab+a+b≤n,我们那他来优化,对于c也是类似
c
<
=
x
−
a
−
b
,
c
≤
(
n
−
a
∗
b
)
/
(
a
+
b
)
c<=x-a-b,c \le (n-a*b)/(a+b)
c<=x−a−b,c≤(n−a∗b)/(a+b) 但既然c是从1数到这么多 那我们直接取min加上去就可以了嘛
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void solve()
{
ll n,x;
scanf("%lld%lld",&n,&x);
ll ans=0;
for(ll a=1;a<=x;a++)
{
for(ll b=1;a+b<x and a*b+a+b<=n;b++)
{
ans+=1ll*(min(x-a-b,(n-a*b)/(a+b)));
}
}
printf("%lld\n",ans);
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
E - Decode
题意
给定一个01字符串 问你有每对(l,r)有多少(x,y)满足这个区间内1和0的个数相等
复盘
赛时其实是想到前缀和了的 然后长度是区间和的两倍就说明满足条件 对于每个满足条件的区间 它包含在多少个区间内 其实就是左右两边取多少乘起来就行 所以赛时是一个
O
(
n
2
)
O(n^2)
O(n2)的算法
在有了D题暴力算法的铺垫之后我觉得我优化一下就交了 结果果然是TLE6
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
string s;
const int N=200010;
int n;
int sum[N];
void solve()
{
cin>>s;
sum[0]=0;
n=s.size();
memset(sum,0,sizeof(sum));
for(ll i=0;i<n;i++)
{
sum[i+1]=sum[i]+(s[i]=='1');
}
ll ans=0;
for(ll i=1;i<=n;i++)
{
for(ll j=i+1;j<=n;j+=2)
{
if(sum[j]-sum[i-1]==(j-i+1)/2)
{
ans+=i*(n-j+1);
ans%=1000000007;
}
}
}
ans%=1000000007;
printf("%lld\n",ans);
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
思路
2倍的关系还是太复杂 有没有什么更加一眼的方式呢?答案是当然的!我们把1看做1,0看做-1,那么一段区间和只要为0那就说明1和0的个数相等,区间和怎么算的,前缀和减出来的嘛!所以我们求完前缀和之后可以保留下标直接排个序,两个数是一样的就说明这个区间是满足条件的,然后再用和上面一样的方法计算出来就可以了
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
string s;
const int N=200010;
int n;
pll sum[N];
void solve()
{
cin>>s;
sum[0]={0,0};
n=s.size();
memset(sum,0,sizeof(sum));
for(ll i=0;i<n;i++)
{
sum[i+1].first=sum[i].first+(s[i]=='0'?-1:1);
sum[i+1].second=i+1;
}
sort(sum,sum+n+1);
ll ans=0;
ll tmp=sum[0].second+1;
for(ll i=1;i<=n;i++)
{
if(sum[i].first==sum[i-1].first)
{
ans+=tmp*(n-sum[i].second+1);
tmp+=sum[i].second+1;
}
else
{
tmp=sum[i].second+1;
}
ans%=1000000007;
}
ans%=1000000007;
printf("%lld\n",ans);
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
F - Bomb
题意
k次操作 每次操作可以选a数组的一个数加到自己的积分里 然后原本的a必须变成a-b 问最终积分的最大值是多少
思路
考虑二分找到一个 x x x使得前 k k k次操作每次添加的积分都大于 x x x 对于每个 x x x我都可以用 O ( n ) O(n) O(n)的时间算出把所有数算到小于 x x x一共要多少次操作 然后再拿去跟 k k k判断即可 最后剩余一小部分直接一个一个算就行了
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
int n,k;
const int N=200010;
int a[N],b[N];
bool check(int mid)
{
ll sum=0;
for(int i=1;i<=n;i++)
{
if(a[i]>=mid)
sum+=(a[i]-mid)/b[i]+1;
}
return sum>=k;
}
void solve()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
}
int l=0,r=1e9+10;
while(r-l>1)
{
int mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
ll ans=0;
ll tmp=0;
for(int i=1;i<=n;i++)
{
if(a[i]>=l)
{
ll t=(a[i]-l)/b[i]+1;
ans+=1ll*t*a[i]-1ll*t*(t-1)/2*b[i];
tmp+=t;
}
}
ans-=1ll*(tmp-k)*l;
printf("%lld\n",ans);
}
int main()
{
int T=1;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}