今日计划2021.10.29:刷CF上的思维题
题意:给一个序列,输出所有的j使得去掉a[j]后的序列当且仅当有一个数是其他所有数的和
枚举。枚举删除每个数是否可行,先将数组排序,可以发现如果删除的数不是最大的数,那么和只可能为最大的数,所以判断总和 sum 在减去最大的数和当前枚举的数后是否等于最大的数,等于则记录答案。
最后额外判断一遍删去最大的数的情况,即 sum在减去最大数和次大数的情况下是否与次大数相同即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,ans[200005],tot;
struct node{int val,id;}a[200005];
bool cmp(node x,node y){return x.val<y.val;}
ll sum;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i].val);
sum+=a[i].val;a[i].id=i;
}
sort(a+1,a+n+1,cmp);sum-=a[n].val;
for(int i=1;i<n;i++)if(sum-a[i].val==a[n].val)ans[++tot]=a[i].id;
if(sum-a[n-1].val==a[n-1].val) ans[++tot]=a[n].id;
printf("%d\n",tot);
sort(ans+1,ans+1+tot);
for(int i=1;i<=tot;i++) printf("%d ",ans[i]);
return 0;
}
题意:给一个序列,求最长子串极差不超过k
st表+尺取法
#include <bits/stdc++.h>
using namespace std;
int n,k,a[100005],l=1,r=1,q[100005],log_2[100005],ans,cnt;
int f[100005][19],g[100005][19],L[100005],R[100005];
int querymax(int l,int r){
int x=log_2[r-l+1];
return max(f[l][x],f[r-(1<<x)+1][x]);
}
int querymin(int l,int r){
int x=log_2[r-l+1];
return min(g[l][x],g[r-(1<<x)+1][x]);
}
void st(){
for(int i=2;i<=n;i++)log_2[i]=log_2[i>>1]+1;
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]);
}
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),f[i][0]=a[i],g[i][0]=a[i];
st();
while(l<=r&&r<=n){
while(querymax(l,r)-querymin(l,r)<=k&&r<n)r++;
while(querymax(l,r)-querymin(l,r)>k&&r>=l)r--;
if(r>n)break;
if(r-l+1>ans)cnt=0,L[++cnt]=l,R[cnt]=r,ans=r-l+1;
else if(r-l+1==ans)L[++cnt]=l,R[cnt]=r;
l++;r++;
}
printf("%d %d\n",ans,cnt);
for(int i=1;i<=cnt;i++)printf("%d %d\n",L[i],R[i]);
return 0;
}
题意:给一个01序列,不能有101的子串,求最少需要将多少个1变为0 (大水题)
贪心。若出现101子串改后一个1为0即可,为什么呢?首先,这个子串不合法了必须改掉,就是改前一个还是后一个的问题,那么改后一个更好,因为对后面还有产生贡献的机会(感性理解一下)
#include <bits/stdc++.h>
using namespace std;
int n,a[105],cnt;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=2;i<n;i++){
if(a[i]==0&&a[i-1]==1&&a[i+1]==1)a[i+1]=0,cnt++;
}
printf("%d\n",cnt);
return 0;
}
4. CF433C Ryouko's Memory Note
题意:给一个序列,可以把序列里所有值为a[i]的元素改成a[j],最多可以改一次,使改后序列相邻元素之差最小
首先有一个结论:
然后就直接对于每一个数将其左右两边的数离线下来,排序,取中位数并用这个中位数去更新答案即可。
注意:对于重复的数将某一个数夹在中间,即 a,b,a这样的形式,b 是需要重复计算的。而对于另一种相邻两个数相等的情况,则不能计入
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[100005];
ll ans,res,ls;
vector<int> v[100005];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++){
if(a[i]!=a[i-1]&&i!=1)v[a[i]].push_back(a[i-1]);
if(a[i]!=a[i+1]&&i!=m)v[a[i]].push_back(a[i+1]);
if(i>1)ans+=abs(a[i]-a[i-1]);
}ls=ans;
for(int i=1;i<=n;i++){
int len=v[i].size();if(!len)continue;
sort(v[i].begin(),v[i].end());res=ls;
for(int j=0;j<len;j++){
res-=abs(i-v[i][j]);res+=abs(v[i][len/2]-v[i][j]);
}ans=min(ans,res);
}
printf("%lld\n",ans);
return 0;
}