BZOJ 5442 Global Warming
题目
给定整数
n
n
n和
x
x
x,以及一个大小为
n
n
n的序列
a
a
a。
你可以选择一个区间
[
l
∼
r
]
[l\sim r]
[l∼r],然后令
a
[
i
]
+
=
d
(
l
≤
i
≤
r
)
a[i]+=d(l\leq i\leq r)
a[i]+=d(l≤i≤r),其中
d
d
d满足
∣
d
∣
≤
x
|d|\leq x
∣d∣≤x。
要求最大化
a
a
a的最长上升子序列的长度,并输出该值
分析
给 [ l ∼ r ] [l\sim r] [l∼r]增加,不优于给 [ l ∼ n ] [l\sim n] [l∼n]增加;给 [ l ∼ r ] [l\sim r] [l∼r]减少,不优于给 [ 1 ∼ r ] [1\sim r] [1∼r]减少,所以正反扫一遍LIS,要维护一个以 i i i结尾时前面不小于 a [ i ] + x a[i]+x a[i]+x的可利用长度,那么一开始正着跑LIS的时候会先记录一次答案,然后把序列取反,反着跑一次LIS,然而在此之前还有 f [ i ] f[i] f[i]种比它大的可利用长度,那么联合现在的LIS统计答案
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=200011;
int f[N],b[N],a[N],n,m,tot,ans;
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
signed main(){
freopen("glo.in","r",stdin);
freopen("glo.out","w",stdout);
n=iut(),m=iut();
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=1,pos;i<=n;++i)
f[i]=lower_bound(b+1,b+1+tot,a[i]+m)-b-1,
b[pos=lower_bound(b+1,b+1+tot,a[i])-b]=a[i],
tot=max(tot,pos);
ans=tot,tot=0;
for (rr int i=n,pos;i;--i)
b[pos=lower_bound(b+1,b+1+tot,-a[i])-b]=-a[i],
ans=max(ans,f[i]+pos),tot=max(tot,pos);
return !printf("%d",ans);
}
洛谷 5307 Mobitel
题目
给定一个
r
r
r行
s
s
s列的矩阵,每个格子里都有一个正整数。
问如果从左上角走到右下角,且每次只能向右或向下走到相邻格子,那么使得路径上所有数的乘积不小于
n
n
n的路径有多少条?
由于答案可能很大,所以请输出答案对
1
0
9
+
7
10^9+7
109+7取模的结果。
分析
考虑容斥,设
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示在
(
i
,
j
)
(i,j)
(i,j)位置乘积为
k
k
k时的路径数,那么就成功的获得了一个TLE的代码
显然需要优化,那么优化转移状态其实只有
2
k
2\sqrt k
2k种可能,所以就可以卡着1s过了
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
const int mod=1e9+7,N=300,M=2000;
int tot,n,m,k,inv[N<<1|15],fac[N<<1|15],rk[M|15],dfn[1000001],dp[2][N|17][M|15];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
signed main(){
freopen("mobitel.in","r",stdin);
freopen("mobitel.out","w",stdout);
inv[0]=fac[0]=inv[1]=fac[1]=1;
for (rr int i=2;i<=N+N;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for (rr int i=2;i<=N+N;++i) inv[i]=1ll*inv[i-1]*inv[i]%mod,fac[i]=1ll*fac[i-1]*i%mod;
n=iut(); m=iut(); k=iut()-1;
for (rr int l=1,r;l<=k;l=r+1)
r=k/(k/l),rk[++tot]=k/l,dfn[k/l]=tot;
dp[0][1][1]=1;
for (rr int i=1;i<=n;++i){
memset(dp[i&1],0,sizeof(dp[i&1]));
for (rr int j=1;j<=m;++j){
rr int x=iut();
for (rr int p=1;p<=tot;++p)
dp[i&1][j][dfn[rk[p]/x]]=mo(dp[i&1][j][dfn[rk[p]/x]],mo(dp[(i&1)^1][j][p],dp[i&1][j-1][p]));
}
}
rr int ans=1ll*fac[n+m-2]*inv[n-1]%mod*inv[m-1]%mod;
for (rr int i=1;i<=tot;++i) ans=mo(ans-dp[n&1][m][i],mod);
return !printf("%d",ans);
}
BZOJ 5443 Lottery
题目
定义两个序列对应位置上不同的值的个数不超过
k
k
k,则可称为
k
k
k相似。
现在有一个长度为
n
n
n的序列
a
a
a,将它划分为
n
−
l
+
1
n−l+1
n−l+1个长度为
l
l
l的子串(第
i
i
i个子串为
a
[
i
]
∼
a
[
i
+
l
−
1
]
a[i]\sim a[i+l-1]
a[i]∼a[i+l−1])
q
q
q组询问,第
j
j
j组询问给出一个
k
j
k_j
kj,求每个子串与多少个其它的子串可称为
k
j
k_j
kj相似。
分析
那么显然整个区间右移一格只会改变两边的值,所以这样就可以暴力求解, O ( n 2 ) O(n^2) O(n2),然而空间会崩掉,然而只有 q q q组询问,所以空间只需要 O ( n q ) O(nq) O(nq)
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=10011;
struct rec{int w,lsrk,rk;}b[111];
int n,a[N],t[111],p[N],sum[111][N],l,m;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline bool cmp1(const rec &x,const rec &y){return x.w<y.w;}
inline bool cmp2(const rec &x,const rec &y){return x.lsrk<y.lsrk;}
signed main(){
freopen("lottery.in","r",stdin);
freopen("lottery.out","w",stdout);
n=iut(); l=iut();
for (rr int i=1;i<=n;++i) a[i]=iut();
m=iut();
for (rr int i=1;i<=m;++i) b[i]=(rec){iut(),i,0};
sort(b+1,b+1+m,cmp1);
for (rr int i=1;i<=m;++i) b[i].rk=i,t[i]=b[i].w;
for (rr int i=0;i<=l;++i) p[i]=lower_bound(t+1,t+1+m,i)-t;
for (rr int i=1;i<=n-l;++i){
rr int srne=0,temp;
for (rr int j=1;j<=l;++j) srne+=a[j]!=a[j+i];
temp=p[srne],++sum[temp][1],++sum[temp][i+1];
for (rr int j=2;i+j+l-1<=n;++j)
srne-=(a[j-1]!=a[i+j-1])-(a[j+l-1]!=a[j+i+l-1]),
temp=p[srne],++sum[temp][j],++sum[temp][j+i];
}
for (rr int i=1;i<=m;++i)
for (rr int j=1;j<=n;++j)
sum[i][j]+=sum[i-1][j];
sort(b+1,b+1+m,cmp2);
for (rr int i=1;i<=m;++i)
for (rr int j=1;j+l-1<=n;++j)
print(sum[b[i].rk][j]),putchar(j+l-1==n?10:32);
return 0;
}