A
给一串序列
可以任意的添加数字
让每个相邻两个数 满足 小的数的两倍 大于等于大的数
求满足条件且添加次数最小的情况
题解思路
一开始还感觉很难 , 认真看了看,直觉 ,直接贪心,不断把 不满足条件中的两个数中的 小的数的两倍加到之间 加到 加不了 (符合两倍小于等于大的数的)就行了。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int main ()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
int n,ans = 0 ;
cin>>n;
int p ;
cin>>p;
for (int i = 2 ; i <= n ; i++ )
{
int k;
cin>>k;
// cout<<p<<" "<<k<<" "<<ans<<"\n";
if ( k > p )
{
if ( k > 2*p )
{
int p1 = p;
while( p1 < k )
{
p1*=2;
ans++;
}
ans--;
}
}else
{
if ( p > 2*k)
{
int p1 = k;
while( p1 < p )
{
p1*=2;
ans++;
}
ans--;
}
}
p = k;
}
cout<<ans<<"\n";
}
return 0 ;
}
B
三个数之间的转换
a b c 每隔一个单位就消耗1
题解思路
最开始想简单写,写着写着把自己写爆炸了,这题花了起码1个多小时,最后之间暴力枚举写的,害。
其实可以考虑线性传递的,隔两个单位看成先到一个再到一个。
这样就有了学长的写法 TQL
AC代码
暴力写法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int main ()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
long long n , c0 = 0 , c1 = 0 , c2 = 0 ,ans = 0 ;
cin>>n;
for (int i = 0 ; i < n ; i++ )
{
int k;
cin>>k;
if ( k % 3 == 0 )
c0++;
else if ( k % 3 == 1 )
c1++;
else
c2++;
}
// cout<<c0<<" "<<c1<<" "<<c2<<"\n";
long long p = ( c0 + c1 + c2 )/3 ;
/*
if ( c0 == c1 && c1 == c2 )
{
cout<<"0\n";
continue;
} */
if ( c0 > p && c1 <= p && c2 <= p )
{
ans = 2*(p - c2) + ( p - c1) ;
}else if ( c0 > p && c1 > p )
{
ans = 2*( c0 - p ) + (c1 - p );
}else if ( c0 > p && c2 > p )
{
ans = ( c0 - p ) + 2*(c2 - p );
}else if ( c1 > p && c0 <= p && c2 <= p)
{
ans = 2*(p - c0) + ( p - c2) ;
}else if ( c1 > p && c2 > p )
{
ans = 2*(c1 - p ) + ( c2 - p );
}else if ( c2 > p && c0 <= p && c1 <= p )
{
ans = 2*( p - c1 ) + (p - c0 );
}
cout<<ans<<"\n";
}
return 0 ;
}
C
某数能不能被1e4以下的两个数(能相同)的立方和合成。
题解思路
我看才1e4,想直接暴力,结果T了,看了下苏佬的代码,双指针居然过了,双指针复杂度大概在 Nlogn左右,比 N平方好多了。双指针也有点二分的感觉。
学长的是二分枚举答案,妙啊,用的太少了,没往那边想。
AC代码
双指针写法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <set>
using namespace std;
const int INF = 0x3f3f3f3f;
set <long long > q;
long long a[20000];
void pr()
{
for (long long i = 1 ; i <= 10000 ; i++ )
{
a[i] = i*i*i;
}
}
int main ()
{
ios::sync_with_stdio(false);
pr();
int t;
cin>>t;
while( t-- )
{
int book = 0,l = 1 ,r = 10000;
long long n;
cin>>n;
while(l <= r )
{
if (a[l] + a[r] < n )
l++;
else if (a[l] + a[r] > n )
r--;
else
{
book = 1;
break;
}
}
if (book)
cout<<"YES\n";
else
cout<<"NO\n";
}
return 0 ;
}
二分枚举答案写法
学长此处用了两个剪枝
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
const int INF = 0x3f3f3f3f;
long long n;
bool che(long long x)
{
long long l = 1 , r = sqrt(x); //对右边界剪枝
while( l <= r )
{
long long mid = (l+r)/2;
long long t = mid*mid*mid;
if ( t > x )
{
r = mid -1;
}else if ( t == x)
return 1;
else
l = mid + 1;
}
return 0;
}
void slo()
{
for (long long i = 1 ; i <= 10000 ; i++ )
{
if ( i*i*i >= n ) //肯定搜不到的时候直接剪枝
break;
long long p = n - i*i*i;
if (che(p))
{
cout<<"YES\n";
return;
}
}
cout<<"NO\n";
}
int main ()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while( t-- )
{
cin>>n;
slo();
}
return 0 ;
}
D
维护一个二叉树。
根节点为最大值,根节点左节点和右节点为左边最大和右边最大。将左节点看成他下面节点的根节点。以此类推。
判断数组中的元素位于二叉树的第几层。
题解思路
数据量小
dfs暴力大法,分两边区间直接找最大值然后继续分区间,直到分不了为止。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int a[200];
int k[200];
int n ;
void dfs(int step , int pit ,int p1,int p2)
{
if ( pit == p1 && p1 == p2 )
{
return ;
}
int t1,t2 = -1 ,t3,t4 = -1 ;
for (int i = p1 ; i < pit ; i++ )
{
if ( t2 < a[i] )
{
t2 = a[i];
t1 = i;
}
}
if ( t2 != -1)
{
k[t1] = step;
dfs(step + 1 , t1 , p1 , pit - 1);
}
for (int i = pit + 1 ; i <= p2 ; i++ )
{
if ( t4 < a[i] )
{
t4 = a[i];
t3 = i;
}
}
if ( t4 != -1 )
{
k[t3] = step ;
dfs(step+1 , t3 , pit + 1, p2 );
}
}
int main ()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
int p1 = 0 ,tmp = -1 ,m = 0 ;
cin>>n;
for (int i = 1 ; i <= n ; i++ )
{
cin>>a[i];
}
for (int i = 1 ; i <= n ; i++ )
{
if (a[i] > tmp )
{
p1 = i;
tmp = a[i];
}
}
k[p1] = m ;
m++;
dfs(m,p1,1,n);
for (int i = 1 ; i <= n ; i++ )
{
cout<<k[i]<<" ";
}
cout<<"\n";
}
return 0 ;
}
E
N个人有代币Ai
可以进行任意次比赛,选择两个人的编号,代币多的人获胜,胜者获得败者的所有代币。当两人代币一样多的时候,随机获胜。
多次比赛后,最后只有一个人获胜
求有可能获胜的人的编号按升序输出。
题解思路
前缀和加贪心。
一开始就想排序,看到要编号,有点怕,就卡死了。看了下学长就解析需要排序,其实就是储存编号麻烦,别怕的。
首先,最后的人一定能获胜,我们可以从后面往前枚举,看看倒数第二个人的前缀和是否比最后一个人的数大,(前缀和即这个人打败所有比他小的人获得的最大值),如果比他大,那么就可以获胜。
然后不断往前比较倒数第三的前缀和和倒数第二的值。
因为如果能打败倒数第二就肯定能得到倒数第二的前缀和,这样就能打败倒数第一了。
小的时候就可以直接break了
以此类推。
学长的思路和我的有点不同,学长是二分枚举第一个获胜的人。原理和上面差不多,也就是直接寻找break之前的那个人。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
struct node
{
int w,p;
}a[200100];
long long sum[200100];
bool cmp (node A, node B)
{
return A.w < B.w;
}
int main ()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
vector <int> ans ;
int n;
cin>>n;
for (int i = 1 ; i <= n ; i++ )
{
int t1;
cin>>t1;
a[i].w = t1;
a[i].p = i;
}
sort(a+1,a+n+1,cmp);
sum[0] = 0 ;
for (int i = 1 ; i <= n ; i++ )
{
sum[i] = ( sum[i-1]+ a[i].w );
}
ans.push_back(a[n].p);
for (int i = n-1 ; i>= 1 ; i--)
{
if ( sum[i] >= a[i+1].w )
{
ans.push_back(a[i].p);
}else
break;
}
sort(ans.begin(),ans.end());
cout<<ans.size()<<"\n";
for (int i = 0 ; i < ans.size() ; i++ )
cout<<ans[i]<<" ";
cout<<"\n";
}
return 0 ;
}
F
给出一段序列,你可以任意删除某个数,使每个数出现的次数相同(0或者N)。
求最小的删除次数。
题解思路
不用map的精彩离散化 前缀和 枚举
没想出来,看学长的代码。离散化的目的是为了求前缀和,而前缀和是为了枚举保留的数。
我们只关心这个数是否和别的数不同和出现的次数。
这样就有了学长sort后求出每个不同的数出现的次数的数组。
为了方便求出枚举保留某个数的次数时,需要删除别的数的次数。
进行了前缀和操作。
后面的枚举基本上就是数学问题了,看好边界就能处理。
因为是让每个数的次数相同,所以必然会趋于某个数的次数,这个数就不用删除。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10 ;
int a[N];
int cnt[N];
int sum[N];
int main ()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for (int i = 0 ; i < n ; i++ )
cin>>a[i];
sort(a,a+n);
int pre = -1;
int pit = 0 ;
for (int i = 0 ; i < n ; i++ )
{
if (pre == -1 )
{
cnt[pit] = 1;
pre = a[i];
}else if ( pre == a[i])
{
cnt[pit]++;
}else
{
pre = a[i];
pit++;
cnt[pit] = 1;
}
}
sort(cnt,cnt+pit+1);
/*
for (int i = 0 ; i <= pit ; i++ )
cout<<cnt[i]<<" ";
cout<<"\n"; */
sum[0] = cnt[0];
for (int i = 1 ; i <= pit ; i++ )
{
sum[i] = sum[i-1] + cnt[i];
}
int minn = sum[pit] - (pit)*cnt[0] -sum[0];
for (int i = 1 ; i <= pit ; i++ )
{
minn = min(minn , (sum[pit] - sum[i]) - (pit-i)*cnt[i] + sum[i-1] );
}
cout<<minn<<"\n";
}
return 0 ;
}