2016"百度之星" - 初赛(Astar Round2B)
RMQ+扫描法
我们预处理RMQ求任意区间的最大值
预处理出以a[i]为最小值 能向左延伸 向右延伸的 L[i], R[i]
那么对于 一个答案 (L[i], R[i]) *rmq(L[i],R[i]) 为此长度的答案,我们可以发现他是可以更新到小于其长度的所有长度答案的,更新就好
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include<map> using namespace std; const int N = 1e5+10, M = 30005, mod = 1e9 + 7, inf = 0x3f3f3f3f; typedef long long ll; ll a[N],dp[N][30],ans[N],las[N]; int l[N],r[N],n; void RMQ_init() { memset(dp,0,sizeof(dp)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); for(int i=1;i<=n;i++) dp[i][0] = a[i]; for(int j=1;(1<<j)<=n;j++) { for(int i=1;i + (1<<j) - 1 <= n; i++) { if(dp[i][j-1] > dp[i+(1<<(j-1))][j-1]) dp[i][j] = dp[i][j-1]; else { dp[i][j] = dp[i+(1<<(j-1))][j-1]; } } } l[1] = 1;a[0] = -1000;a[n+1] = -10000; for(int i=2;i<=n;i++) { int tmp = i-1; while(a[i]<=a[tmp]) tmp = l[tmp]-1; l[i] = tmp+1; } r[n] = n; for(int i=n-1;i>=1;i--) { int tmp = i+1; while(a[i]<=a[tmp]) tmp = r[tmp] + 1; r[i] = tmp - 1; } } ll rmq(int l,int r) { if(l==r) return a[l]; int k = (int) (log((double) r-l+1) / log(2.0)); return max(dp[l][k], dp[r - (1<<k) + 1][k]); } int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%I64d",&a[i]); RMQ_init(); for(int i=1;i<=n;i++) ans[i] = -1; for(int i=1;i<=n;i++) { ll mm = rmq(l[i],r[i]); ans[r[i]-l[i]+1] = max(mm*a[i],ans[r[i]-l[i]+1]); } las[n+1] = -1; for(int i=n;i>=1;i--) { las[i] = max(ans[i],las[i+1]); } for(int i=1;i<=n;i++) printf("%I64d\n",las[i]); } return 0; }
1003 瞬间移动:
组合数学
#include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<stack> #include<map> #define ll __int64 #define mod 1000000007 using namespace std; ll poww(ll a,ll n) { ll r=1,p=a; while(n) { if(n&1) r=(r*p)%mod; n>>=1; p=(p*p)%mod; } return r; } ll coun(ll n,ll m) { ll sum=1; for(ll i=1,j=n;i<=m;i++,j--) { sum*=j; sum%=mod; sum*=poww(i,1000000005); sum%=mod; } return sum; } ll x,y,z,t; ll n,k; ll ans; int main() { while(scanf("%I64d %I64d",&x,&y)!=EOF) { ans=0; n=(x+y)-4; k=x-2; ans=coun(n,k); cout<<ans<<endl; } return 0; }
1006 中位数计数
我也不知道分类
枚举以a[i]为中位数, 重构一个数组,小于a[i]的是-1,大于a[i]的是1,等于a[i]是0;,
这个问题就变成了求这个数组有多少个区间包含0,且区间和是0的个数,这个用前缀解决
map超时
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cmath> #include<queue> #include<map> using namespace std; const int N = 1e5+20, M = 1e4, mod = 1000000007,inf = 1e9; typedef long long ll; int a[N],b[N],n,now[N]; int H[N]; ll ans[N]; int f[N],h[N]; int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i] = a[i]; for(int i=1;i<=n;i+=1) { memset(f,0,sizeof(f)); memset(h,0,sizeof(h)); for(int j=1;j<=n;j++) if(a[i]==a[j]) now[j] = 0; else if(a[i]>a[j]) now[j] = 1; else if(a[i]<a[j]) now[j] = -1; int last = 0,mx = 0; ll aa = 1; for(int j=i-1;j>=1;j--) f[last+now[j]+M]++,last +=now[j],mx = max(mx,last); last = 0; for(int j=i+1;j<=n;j++) h[last+now[j]+M]++,last +=now[j],mx = max(mx,last); for(int j=1;j<=mx;j++) aa += ((ll)f[j+M]*(ll)h[-j+M]),aa+=((ll)f[-j+M]*(ll)h[j+M]); aa+=f[0+M]; aa+=h[0+M]; aa+=(ll)f[0+M]*(ll)h[0+M]; ans[i] = aa; } for(int i=1;i<n;i+=1) { printf("%I64d ",ans[i]); } printf("%I64d\n",ans[n]); } return 0; }