A题 题目链接 (签到)
题意:给定q个询问,每次给出左右跳的距离,给出跳的次数k,规定先向右跳。
每次询问最终的位置。 (1<=q<=1e4)
思路:直接上就好
//#pragma comment(linker, "/STACK:102400000,102400000")//手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f3f;
const double eps=1e-5;
const int maxn=2e5+7;
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
int T;
cin>>T;
while(T--)
{
int a,b,k;
LL ans=0;
scanf("%d%d%d",&a,&b,&k);
int times=k/2;
ans+=1ll*times*(a-b);
if(k&1) ans+=a;
printf("%lld\n",ans);
}
// system("pause");
return 0;
}
B题 题目链接 (贪心)
题意:给定一个0 1组成的数列,问最少把多少个不同的1改成0 可以使得不出现1 0 1 这种子段。
(1<=n<=2e5)
思路:从左到右遍历,对于遇到的两边都是1的0,我们把它右边的1改成0即可。
//#pragma comment(linker, "/STACK:102400000,102400000")//手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f3f;
const double eps=1e-5;
const int maxn=2e2+7;
//题意:给定一个0 1组成的数列,问最少把多少个不同的1改成0 可以使得不出现1 0 1 这种子段
int n,a[maxn];
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
}
for(int i=2;i<n;i++)
{
if(!a[i] && a[i-1] && a[i+1])
{
a[++i]=0;
ans++;
}
}
cout<<ans;
// system("pause");
return 0;
}
C题 题目链接 (map+思维)
题意:给定一个数列,问是否可以去除1个元素使得剩余元素构成完美数列,完美数列指:存在一个元素是其余元素的和。求出满足条件的所有元素下标。
(1<=n<=2e5)
思路:完美数列的特点就是,数列和等于某个元素值的两倍。 用map把所有元素的两倍都存一下(如果这个值只出现一次,映射成下标,出现多次,映射成INF)。然后枚举去除每个元素,看其它元素的和是否在map中出现,且映射不是当前元素下标。
//#pragma comment(linker, "/STACK:102400000,102400000")//手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f3f;
const double eps=1e-5;
const int maxn=1e6+7;
map<LL,int> mp;//存在多个就是INF 否则是index
LL sum=0;
int n,a[maxn];
vector<int> ans;
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
//把这个数的两倍存一下 如果某个数列的和就是这个数的两倍,并且这个数在数列中
if(!mp[a[i]*2ll]) mp[a[i]*2ll]=i;
else mp[a[i]*2ll]=INF;
sum+=a[i];
}
for(int i=1;i<=n;i++)
{
LL t=sum-a[i];
int x=mp[t];
//inf说明出现多次 ,映射为i的时候,说明其余元素的和是a[i]的两倍,不符合完美数列
if(x==INF || (x && x!=i)) ans.push_back(i);
}
printf("%d\n",ans.size());
for(auto i:ans) printf("%d ",i);
// system("pause");
return 0;
}
D题 题目链接 (贪心+优先队列)
题意:给定数列s,给定正整数k,问s中的元素最多能构成多少个长度为k,且元素 相同的数列t。s=[1,2,3,2,4,3,1] 能构成2个 [1,2,3]。
(1<=n,k<=2e5)
思路:
问题可以等价为:选k个数,每个数在数组中出现次数的最小值要最大。(如果k个当中有若干个数相同,那每个数出现次数为总次数/被选次数)
贪心,每次选出出现次数最大的数,如果被过了,那么次数要除以2再加入优先队列。
如果被选了x次,那么要除以x。
这题我们首先把s中的元素按出现次数放入优先队列。加一个times存每个元素被选择的次数,v表示在s中出现的次数。 根据贪心,每次我们选择v/times最大的,然后把对应元素的times+1,重新放入优先队列,循环k次即可。
//#pragma comment(linker, "/STACK:102400000,102400000")//手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f3f;
const double eps=1e-5;
const int maxn=2e5+7;
int book[maxn];
int n,k,a[maxn];
struct pr
{
int v,index;//出现次数和数列元素
int times;//被选择的次数
friend bool operator < (const pr &a,const pr &b)
{
return a.v/a.times<b.v/b.times;
}
}p[maxn];
int cot;
vector<int> ans;
priority_queue<pr> q;
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
book[a[i]]++;
}
for(int i=1;i<=200000;i++)
{
pr t;
if(book[i])
{
t.v=book[i];//出现次数
t.index=i;//index为数列元素
t.times=1;//初始化成第一次选择
q.push(t);
}
}
while(k--)
{
pr t=q.top();
q.pop();
ans.push_back(t.index);//index存的是对于的元素
t.times++;//选择的次数+1
q.push(t);
}
for(auto i:ans) printf("%d ",i);
// system("pause");
return 0;
}
E题 题目链接(二分 / dp)
题意:给出n个题目,每个题目都有一个主题,现在让你安排若干场考试,每场考试的题目数量恰好是前一场的2倍,并且
他们的主题是相同的,问最多能用掉多少题目?
思路:先把数列元素离散化,按个数递增排序。接下来有两种做法。
①:二分。 从1开始枚举初始场的题目,最大为mmax,即所有种类中最多的题目数,设为i。 然后二分找2i 4i…,如果没找到,就break。 更新ans。
时间复杂度为nlogn
//#pragma comment(linker, "/STACK:102400000,102400000")//????
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const LL INF=1e18+7;
const double eps=1e-5;
const int maxn=2e5+7;
//对于每个比赛 题目主题相同且为比赛主题
//不同比赛的主题不同
map<int,int> mp;
int n;
vector<int> a;
int ans=0;
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
mp[t]++;
}
for(auto i:mp) a.push_back(i.second);
sort(a.begin(),a.end());
int len=(int)a.size(),mmax=a[len-1];
for(int i=1;i<=mmax;i++)
{
int st=i,tmax=0;
int in=0;//记录二分找的位置
for(;st<=mmax;st*=2)
{
int pos=lower_bound(a.begin()+in,a.end(),st)-a.begin();
if(in==len) break;
tmax+=st;
in=pos+1;//更新下次二分的初始位置
}
ans=max(ans,tmax);
}
cout<<ans;
// system("pause");
return 0;
}
②:dp。 记排序后题目种类数为0~len-1 ,dp[i][j]表示只考虑[i,len-1],第i种题目作为初始场并选j题的状态。
可以滚动数组优化第一维
dp[j]=dp[j<<1]+j
sort(a.begin(),a.end());
int len=a.size();
for(int i=len-1;i>=0;i--)
{
for(int j=1;j<=a[i];j++)
{
dp[j]=j+dp[j<<1];
ans=max(ans,dp[j]);
}
}
cout<<ans;
//#pragma comment(linker, "/STACK:102400000,102400000")//????
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const LL INF=1e18+7;
const double eps=1e-5;
const int maxn=2e5+7;
//对于每个比赛 题目主题相同且为比赛主题
//不同比赛的主题不同
map<int,int> mp;
int n;
vector<int> a;
int ans=0;
int dp[maxn<<1];//从后往前更新 dp[i][j]=dp[i+1][j*2]+j
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
mp[t]++;
}
for(auto i:mp) a.push_back(i.second);
sort(a.begin(),a.end());
int len=a.size();
for(int i=len-1;i>=0;i--)
{
for(int j=1;j<=a[i];j++)
{
dp[j]=j+dp[j<<1];
ans=max(ans,dp[j]);
}
}
cout<<ans;
// system("pause");
return 0;
}
F题
题目链接 (单调队列优化dp)
(F1 F2都是一样的题,不过F1的数据更小可以暴力,F2需要用单调队列)
题意:给定长度为n的数列,取出x个数,要求每个长度为k的连续子段都要有取到,问x个数的和最大为多少。
思路:这题和烽火传递思路基本一样。只不过有x限制,需要加一维。
dp[i][j]表示只考虑前i个数,第i个数取,并且包括i在内已经取了j个数的情况。
转移方程: dp[i][j]=max(a[i]+dp[m][j-1],dp[i][j]) ( i-k<=m<=i-1 )
m的范围很好理解,比如i=7,k=3,那么在我们考虑i=7之前,4 5 6必须取一个,所以就是i-k<=m<=i-1
for(int i=2;i<=n;i++)
{
for(int j=1;j<=min(i,x);j++)
{
for(int m=max(0,i-k);m<=i-1;m++)
{
dp[i][j]=max(dp[i][j],dp[m][j-1]+a[i]);
}
}
}
这样的话时间复杂度最高是O(n²)
在求[i-k,i-1]上dp的最大值我们可以用上单调队列进行维护。
//#pragma comment(linker, "/STACK:102400000,102400000")//手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<deque>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const LL INF=1e18;
const double eps=1e-5;
const int maxn=5e3+7;
//题意:给定长度为n的数列,取出x个数,要求每个长度为k的连续子段都要有取到,问x个数的和最大为多少
LL dp[maxn][maxn];
//dp[i][j]=max(dp[l][j-1],dp[i][j]) i-k<=l<=i-1
int n,k,x,a[maxn];
deque<int> q;
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d%d%d",&n,&k,&x);
for(int i=1;i<=n;i++) scanf("%d",a+i);
fill(dp[0],dp[0]+maxn*maxn,-INF);
dp[0][0]=0;//0的边界不能丢
for(int j=1;j<=x;j++)
{
q.clear();
q.push_back(0);//队列初始不能为空
for(int i=1;i<=n;i++)
{
dp[i][j]=max(dp[i][j],dp[q.front()][j-1]+a[i]);
//i入队时,先把小于它的丢出去
while(q.size() && dp[q.back()][j-1]<dp[i][j-1]) q.pop_back();
q.push_back(i);
//出队,把太久远的也丢出去
while(q.size() && q.front()<i+1-k) q.pop_front();
}
}
LL ans=-1;
for(int i=n;i>n-k;i--) ans=max(ans,dp[i][x]);
cout<<ans;
// system("pause");
return 0;
}