HDU 5289
http://acm.hdu.edu.cn/showproblem.php?pid=5289
题意:给定一串数字,问有几个区间内最大值与最小值的差小于k
思路:刚开始自己做的时候用rmq算出区间最大和最小,然后暴力O(n2)查找果断T。 其实查找这块暴力枚举左端点,然后二分右端点。
O(nlogn)过
反思:这个二分自己搞的时候还错了。哎 ,太菜了我
#include <iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn=100100;
int a[maxn];
int dpmin[maxn][30];
int dpmax[maxn][30];
int mm[maxn];
int n,kk;
ll ans=0;
void st(int n)
{
mm[0]=-1;
for(int i=1;i<=n;i++)
{
dpmin[i][0] = dpmax[i][0] = a[i];
mm[i] = ((i&(i-1))== 0 )? mm[i-1]+1: mm[i-1];
}
for(int j=1;j<=mm[n];++j)
{
for(int i=1;i+(1<<j)-1<=n;++i)
{
dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]);
dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]);
}
}
}
int rmq(int x,int y)
{
int k = mm[y-x+1];
return max(dpmax[x][k], dpmax[y-(1<<k)+1][k])-min(dpmin[x][k], dpmin[y-(1<<k)+1][k]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&kk);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
ans=0;
st(n);
for(int i=1;i<=n;++i)
{
int ll=i, rr=n;
while(ll<=rr)
{
int mid=(ll+rr)>>1;
if(rmq(i,mid)>=kk) rr=mid-1;
else ll=mid+1;
}
if(rmq(i,rr)<kk) ans+=(rr-i+1);
else ans+=(ll-i+1);
}
printf("%I64d\n",ans);
}
return 0;
}
HDU 3183
http://acm.hdu.edu.cn/showproblem.php?pid=3183
题意:给你一串0-9内的串,可以删除m个,删除完的顺序还是原顺序。使得剩下的数最大
思路:问题转换为我们在n的字符串中顺序选n-m个数字使得串最大,那么我们可以选的数字范围是:
(上一个选的数字后一个数字,右边界减去还要选的数再减一)(x,n-need-1);那跑n-m次rmq的查询即可。输出的时候从第一个不为0数的开始输出
#include <iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
using namespace std;
char s[1100];
char ans[1100];
int dp[1100][1100];
int m,len;
int mi(int i,int j)
{
return s[i]<=s[j]? i:j;
}
void st()
{
for(int i=0;i<len;++i)
{
dp[i][0]=i;
}
for(int j=1;j<=(int)(log((double)len)/log(2.0));++j)
{
for(int i=0;i+(1<<(j-1))-1<len;i++)
{
dp[i][j]=mi(dp[i][j-1],dp[i+(1<<(j-1))][j-1] );
}
}
}
int qurry(int l,int r)
{
int k=(int)(log((double)(r-l+1))/log(2.0));
return mi(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
while(scanf("%s%d",s,&m)!=EOF)
{
len=strlen(s);
st();
int need=len-m;
int in=0,now=0;
while(need--)
{
in=qurry(in,len-need-1);
ans[now++]=s[in++];
}
int i=0;
for(i=0;i<now;++i)
{
if(ans[i]!='0') break;
}
if(i==now)
{
printf("0\n"); continue;
}
else
{
for(int j=i;j<now;++j)
{
printf("%c",ans[j]);
}
printf("\n");
}
}
return 0;
}
HDU 5726
http://acm.hdu.edu.cn/showproblem.php?pid=5726
题意:给一串数字,m次询问问(l,r)的GCD,同时输出有相同GCD的区间个数
思路:和上一道题其实差不多就是二分加记录数值即可;(GCD是递减的)
#include <iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=100005;
int dp[maxn+5][35];
int a[maxn];
int mm[maxn];
int n;
void rmq()
{
mm[0]=-1;
for(int i=1;i<=n;++i)
{
mm[i]=(i&(i-1))==0?mm[i-1]+1:mm[i-1];
dp[i][0]=a[i];
}
for(int j=1;j<=mm[n];++j)
{
for(int i=1;i+(1<<j)-1<=n;++i) //要有大于 2^j 个数字 于是有 n-i+1>=2^j;
{
dp[i][j]=__gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r)
{
int k=mm[r-l+1];
return __gcd(dp[l][k],dp[r-(1<<k)+1][k]);
}
int bs(int gcd,int i)
{
int l=i,r=n;
while(l+1<=r)
{
int mid=(l+r)>>1;
int g=query(i,mid);
if(__gcd(g,gcd)>=gcd) l=mid+1; //如果大于的话会多移1
else r=mid-1;
}
if(__gcd(gcd,query(i,l))!=gcd) l--; //如果不等于说明l左移了1
return l;
}
int main()
{
int ca=1;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
rmq();
map<int, ll> my;
for(int i=1;i<=n;++i)
{
int pos=i,gcd=a[i];
while(pos<=n)
{
int pos1=bs(gcd,pos);
my[gcd]+=(pos1-pos+1);
pos=pos1+1;
gcd=__gcd(gcd,a[pos]);
}
}
int m;
scanf("%d",&m);
printf("Case #%d:\n",ca++);
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
int gcd=query(l,r);
printf("%d %lld\n",gcd,my[gcd]);
}
}
return 0;
}