一:整体评价
出题难度
这场本来就是想多出点之前的算法的,没想到会打成这样,刚好让你们意识到自己算法也要多练。
这场都打得很差感觉,差距太大了,自己慢慢补吧。算法也不能忘记。
二:题解
A 最最最最长(我真的好长)~~~~~~~上升子序列咧咧咧咧
思路
本题是对大家思维的一个考察。我们不难想到如果把b序列逆序插入对于a序列来说影响是较小的,会使得对a序列的贡献最多为1。那我们同时要考虑,如何把这个贡献1也消掉。
那我们不妨把a序列中的元素和b序列中的元素逐一进行比较,谁大谁就先插入,从而做到对a数组的贡献最小。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=998244353;
const int MX=0x3f3f3f3f3f3f3f3f;
int n,m;
int b[500005];
int a[500005];
bool cmp(int x,int y){
return x>y;
}
void icealsoheat(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int j=1;j<=m;j++){
cin>>b[j];
}
sort(b+1,b+1+m,cmp);
int id=1;
for(int i=1;i<=n;i++){
while(id<=m&&b[id]>=a[i]){
cout<<b[id]<<" ";
id++;
}
cout<<a[i]<<" ";
}
while(id<=m){
cout<<b[id]<<" ";
id++;
}
cout<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie();
cout.tie();
int _yq;
_yq=1;
cin>>_yq;
while(_yq--){
icealsoheat();
}
}
B 这该用啥算法?
思路
这题主要就是贪心二分,因为如果有一个x可以放置那么说明对于更小的数也可以所以说是满足二分的性质的,然后知道二分之后就要像check函数怎么写了,贪心的想最优解肯定是从后往前跑,直接把多余的分出去,在分第i个的时候如果前面分的已经够x个了那就把原来的全分过去,如果不够就分多余的分过去,然后最后跑一遍判断是否都有至少x个。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int a[N];
int b[N];
int n;
int check(int x){
for(int i=1;i<=n;i++)b[i]=0;
for(int i=n;i>=3;i--){
if(b[i]>x){
int tmp=a[i]/3;
b[i-1]+=tmp;
b[i-2]+=tmp+tmp;
}
else {
int tmp=b[i]+a[i];
tmp-=x;
if(tmp<0)return 0;
tmp/=3;
b[i-1]+=tmp;
b[i-2]+=tmp+tmp;
b[i]=x;
}
}
b[1]+=a[1];
b[2]+=a[2];
for(int i=1;i<=n;i++){
if(b[i]<x)return 0;
}
return 1;
}
void vision()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l=1,r=1e9;
while(l<=r){
int mid=(l+r)/2;
if(check(mid))l=mid+1;
else r=mid-1;
}
cout<<r<<"\n";
return ;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
cin>>t;
while(t--){
vision();
}
return 0;
}
C 屠龙勇士
思路
题意读明白了话其实很简单,就是一个判断点在直线的哪侧,给你三个点,判断第三个点在在前两个点所构成的直线的哪侧,这里我们用向量叉积来判断(这好像是高中的知识吧)
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
double x1,y1,x2,y2,x3,y3;
cin>>x1>>y1>>x2>>y2>>x3>>y3;
if((y2-y1)*(x3-x2)==(y3-y2)*(x2-x1))
{
printf("TOWARDS\n");
}else if((y2-y1)*(x3-x2)>(y3-y2)*(x2-x1))
{
printf("RIGHT\n");
}else{
printf("LEFT\n");
}
return 0;
}
D 一道好玩的算法题
思路
就按照题意模拟就可以了,这题主要是看之前讲的有没有都掌握。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int pr[N];
int xpr[N];
int n;
int q;
struct vis{
int x,id;
};
vis a[N];
int cmp(vis x,vis y){
if(x.x==y.x)return x.id<y.id;
return x.x<y.x;
}
void vision(){
cin>>n;
cin>>q;
for(int i=1;i<=n;i++){
cin>>a[i].x;
a[i].id=i;
pr[i]=pr[i-1]+a[i].x;
xpr[i]=xpr[i-1]^a[i].x;
}
sort(a+1,a+1+n,cmp);
while(q--){
int op;
cin>>op;
if(op==1){
int x,y;
cin>>x>>y;
cout<<pr[y]-pr[x-1]<<"\n";
}
else if(op==2){
int key;
cin>>key;
int l=0,r=n+1;
while(l+1<r){
int mid=(l+r)/2;
if(a[mid].x>=key)r=mid;
else l=mid;
}
cout<<a[r].id<<"\n";
}
else if(op==3){
int x,y;
cin>>x>>y;
cout<<(xpr[y]^xpr[x-1])<<"\n";
}
else if(op==4){
cout<<"haowan\n";
}
}
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--){
vision();
}
return 0;
}
E 圣遗物计算器
思路
想出一道dfs但是没有好的想法,然后随便出了一题虽然五层循环暴力也能过,算考验代码能力吧。就正常的每种都跑循环然后判断就可以
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
struct vis{
int a,b,c,d;
};
vis w[10][100];
int cnt[10];
int asa,asb,asc,asd;
int p=0;
void dfs(int ww,int ta,int tb,int tc,int td){
if(ww==6){
if(ta>=asa&&tb>=asb&&tc>=asc&&td>=asd){
p=1;
return ;
}
return ;
}
for(int i=1;i<=cnt[ww];i++){
dfs(ww+1,ta+w[ww][i].a,tb+w[ww][i].b,tc+w[ww][i].c,td+w[ww][i].d);
if(p==1)return ;
}
}
void vision()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
int tmp;
cin>>tmp;
cnt[tmp]++;
cin>>w[tmp][cnt[tmp]].a>>w[tmp][cnt[tmp]].b>>w[tmp][cnt[tmp]].c>>w[tmp][cnt[tmp]].d;
}
cin>>asa>>asb>>asc>>asd;
// cout<<cnt[1]<<" "<<cnt[2]<<" "<<cnt[3]<<" "<<cnt[4]<<" ";
// cout<<asa<<" "<<asb<<" "<<asc<<" "<<asd<<" ";
dfs(1,0,0,0,0);
if(p==1)cout<<"YES\n";
else cout<<"NO\n";
return ;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--){
vision();
}
return 0;
}
F 千玺!打响指!
思路
自己先意会吧
代码
cin >> n;
string s;
cin >> s;
int num0 = 0;
for(auto c : s)
{
num0 += c == '0';
}
int num1 = n - num0;
reverse(s.begin(), s.end());
int now = 0;
LL res = 0;
int i = 0;
while(i < n)
{
if(s[i] == '0')
{
res += i - now;
now ++;
cout << res << ' ';
}
i ++;
}
lop(j, now, n)
{
cout << -1 << ' ';
}
cout << el;
G 叶熊赛跑
思路
其实我们可以想到,如果我们想让最终获奖的人最多,那就是把所有有可能获奖的人都算上。
如果一个先出发的序号被后面的序号追上,没在后面出发的序号前面回来,那这个前面的序号一定没有获奖的可能。那么其它的序号,就有可能获奖。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=998244353;
const int MX=0x3f3f3f3f3f3f3f3f;
int n,m;
int b[1000005];
struct we{
int x,id;
bool operator <(const we &k)const{
return x<k.x;
}
}hh[1000005];
void icealsoheat(){
cin>>n;
int ans=0;
int id=1;
for(int i=1;i<=n;i++){
cin>>hh[i].x;
hh[i].id=i;
}
sort(hh+1,hh+1+n);
int maxx=0;
for(int i=1;i<=n;i++){
if(maxx<hh[i].id){
ans++;
}
maxx=max(maxx,hh[i].id);
}
cout<<ans<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie();
cout.tie();
int _yq;
_yq=1;
cin>>_yq;
while(_yq--){
icealsoheat();
// else break;
}
}
H 小博怡情
思路:
结论:放在次小的位置,能必胜。 证明:
对于当前位置,如果它的左边,没有存在比它小的元素,那么此时Bob无法移动,Bob胜利。
对于当前位置,如果它的左边,存在2个以上比它小的元素,那么Bob可以选择将木块放在次小的位置,使得Alice操作后只能放在最小的元素,导致Bob必胜。也就是说以ai为起点的最长上升子序列的长度为2时才满足。
代码
I wkw玩拼图
思路
这题的证明用到了鸽巢定理,笼统来说,如果数据范围小,我们的双重for循环就可以跑完;如果数据范围大,那么序列中出现的数越多,他为了不让答案出现,不能出现的数就越多,最后导致一定会出现能形成答案的数,所以不用跑完for循环便可以找到答案
代码
J 扫描线
思路
通过观察可以发现矩形的长和宽都在1000以内
所以考虑采用二维前缀和去维护这n个矩形
每次询问的时候可以O(1)查询
故总的时间复杂度为O(1000*1000*t+q);
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;
const int N = 1e6+ 5;
PII A[N];
int ans[1005][1005];
void solve()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
{
int l,r;
cin>>l>>r;
ans[l][r]+=l*r;
}
for(int i=1;i<=1000;i++)
{
for(int j=1;j<=1000;j++)
{
ans[i][j]+=ans[i][j-1]+ans[i-1][j]-ans[i-1][j-1];
}
}
while(q--)
{
int l1,l2,r1,r2;
cin>>l1>>r1>>l2>>r2;
int an=ans[l2-1][r2-1]-ans[l1][r2-1]-ans[l2-1][r1]+ans[l1][r1];
cout<<an<<"\n";
}
for(int i=1;i<=1000;i++)
{
for(int j=1;j<=1000;j++)
{
ans[i][j]=0;
}
}
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
K 狼吃羊
思路
从贪心的角度考虑,Wolf不会选择一个方向后是不会往回走的
故就是求长度为k的区间和最大。
采用前缀和维护然后遍历
时间复杂度为O(n)
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e5 + 5;
int A[N],sum[N];
void solve()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>A[i];
sum[i]=sum[i-1]+A[i];
}
int ans=0;
for(int i=k;i<=n;i++)
{
ans=max(ans,sum[i]-sum[i-k]);
}
cout<<ans<<"\n";
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}