这个周首先复习了暑假看的树状数组
#define lowbit(x) ((x)&-(x))
void add(int x,int d) // 初始化(或更改树状数组),如果想初始化那就是多次更改
{
while(x<=n)
{
tree[x]+=d;
x+=lowbit(x);
}
}
int sum(int x) //求数组和的优化
{
int sum=0;
while(x>0)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
P1168 中位数
给出一段序列,要求对于每奇数个数就要求一次中位数,n最大有10的5次方。
这个题首先想到的就是sort排序,但是每次要求中位数的时候就sort一次肯定就超时了,自己写了个二分答案也超时了…看到题解有大佬用动态数组+二分水过去了,对于每个数字,用二分找到它应该在的位置,插入到前面的数组里,每次直接输出数组中间的数就可以了(stl的二分,水题十分好用)。
#include
using namespace std;
vector<int>a;
int main()
{
int n,i,x;
cin>>n;
for(i=1;i<=n;i++)
{
cin>>x;
a.insert(upper_bound(a.begin(),a.end(),x),x);
if(i%2!=0)
{
cout<<a[(i-1)/2]<<endl;
}
}
}
B. Hemose Shopping
给出几组数据,对于每组数据有数组大小n,整数x,数组的数字。每个数组,如果|ai-aj|>=x,那么可以交换数组中的这两个数字,能不能将它们交换成升序?用yes和no代表输出。
纯思维题,但是想了很久。如果数组的大小除2要大于等于x,就肯定可以交换,如果小于x,我们还要考虑这个数组原本是否有序。如果这个数组在n-x到x的元素原来就有序,那么它仍可以交换;如果无序,那么就没办法了。
#include
using namespace std;
typedef long long ll;
int a[100001];
int b[100001];
int main()
{
int i,n,x,t;
cin>>t;
while(t--)
{
cin>>n>>x;
for(i=0;i<n;i++)
{cin>>a[i];
b[i]=a[i];
}
if(n/2>=x) {cout<<"YES"<<endl;}
else {
sort(a,a+n);
int k=1;
for(i=n-x;i<x;i++)
{
if(b[i]==a[i]) continue;
else {k=0;break;}
}
if(k==0) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
}
}
P1020 导弹拦截
通俗点说就是给出一段导弹的高度,第一个输出求出最长不上升子序列的长度,第二个输出最长上升子序列的长度。
想要得到满分的话要用复杂度为n*log2 n的方法,我用了二分查找和树状数组两种方法做出来了。
1.二分查找
网上有stl的二分和手写二分两种方法,我这里用的是手写的二分
建议不太懂的直接记模板,通过这种写法最长上升子序列、不上升子序列、下降子序列、不下降子序列只要改两个符号就可以实现转换。
#include
using namespace std;
int a[100001],f[100001];
int ans=0;
int main()
{
int n=0,x;
while(cin>>x)
{
n++;
a[n]=x;
}
f[0]=999999;
for(int i=1; i<=n; ++i)
{
if(a[i]==0)
continue;
if(f[ans]>=a[i]) //想要改成最长下降子序列把等号去掉就行
f[++ans]=a[i];
else
{
int l=0,r=ans,mid;
while(l<=r)
{
mid=(l+r)/2;
if(f[mid]<a[i])
r=mid-1;
else
l=mid+1;
}
if(l!=0)
f[l]=a[i];
}
}
cout<<ans<<endl; //最长不上升子序列
memset(f,0,sizeof(f));
f[0]=0;
ans=0;
for(int i=1; i<=n; ++i)
{
if(a[i]==0)
continue;
if(f[ans]<a[i]) //想要改成最长不下降子序列加个等号就可以了
f[++ans]=a[i];
else
{
int l=0,r=ans,mid;
while(l<=r)
{
mid=(l+r)/2;
if(f[mid]>=a[i])
r=mid-1;
else
l=mid+1;
}
if(l!=0)
f[l]=a[i];
}
}
cout<<ans<<endl; //最长上升子序列
return 0;
}
2.树状数组
虽然很久以前就学了树状数组,但是现在依然还是不怎么会用,看完大佬的代码我只能说是:妙啊,真是妙啊!
这里就解释一下最长上升子序列吧,另一个直接倒转其实就可以了。树状数组有两个函数,一个是用来初始化(或更改)树状数组的函数,另一个是通过树状数组来优化答案的函数。我们运用dp的思想,把导弹高度h作为树状数组的下标,用树状数组的值来记录最长的长度。对一个高度进行操作,我们要求得这个高度之前的最长上升长度,遍历树状数组记录下最大值就行。然后将这个高度插入树状数组,因为是最长上升子序列,所以树状数组下标比这个高度高的长度都要+1,然后求这个高度的最长子序列长度是多少,更改树状数组。控制范围,我们提前记录下导弹高度的最小值和最大值。
#include
#define lowbit(x) ((x)&-(x))
using namespace std;
int h[100001],shangsheng[100001],xiajiang[100001];
int maxx=0,n=0,m,minn=999999;
int zuichang(int x) //最长上升子序列的计算函数
{
int res=0;
while(x>=minn) //求这个高度之前的最长上升长度是多少
{
res=max(shangsheng[x],res);
x=x-lowbit(x);
}
return res;
}
void addzuichang(int x,int y) //最长上升子序列树状数组的插入函数
{
while(x<=maxx) //比这个高度高的都要重新更改一下树状数组的值
{
shangsheng[x]=max(shangsheng[x],y);
x=x+lowbit(x);
}
}
int buxiajiang(int x)
{
int res=0;
while(x<=maxx)
{
res=max(res,xiajiang[x]);
x=x+lowbit(x);
}
return res;
}
void addbuxiajiang(int x,int y)
{
while(x>=minn)
{
xiajiang[x]=max(xiajiang[x],y);
x=x-lowbit(x);
}
}
int main()
{
int i;
int ans1=0,ans2=0;
while(cin>>m)
{
n++;
h[n]=m;
maxx=max(maxx,m); //提前求出范围
minn=min(minn,m);
}
for(i=1;i<=n;i++)
{
int x=zuichang(h[i])+1; //用函数求出的是它之前的最长长度,算上它自己还要+1
int y=buxiajiang(h[i])+1;
ans1=max(ans1,y);
ans2=max(ans2,x); //求出求过的最大值
addzuichang(h[i]+1,x);//因为是严格上升,高度还要+1
addbuxiajiang(h[i],y);//不上升就行了,比这个高度低的都可以更改
}
cout<<ans1<<endl;
cout<<ans2<<endl;
}