省选测试38
作者:互联网
选拔赛
-
先给a数组和c数组从大到小排序,D[l][r]表示分配完前K个人(A组)大于等于l,后n-K个(B组)小于等于r,枚举A组的最小分数L,D[L][L]-D[L+1][L]的和即为答案,D[L][L]和D[L+1][L]直接每次用每次dp即可,总复杂度 n^4
-
我们会发现随着c数组中数值的减小,对于A组,能匹配的人数在减小,对于B组,能匹配的人数在增多,我们可以把当前的c[i]给还未匹配的,能匹配c最少的A组人或B组人,这样预处理出c[i]能匹配的A组人数k[i],以及能匹配的B组人数z[i],再定义dp[i][j]表示前i个c,给了A组j个的方案数,就能转移了。
-
通过讨论c[i]给A还是给B,知道这是给A/B的第几个c,也就知道转移系数(当前的c[i]的贡献)了,自己理解的不太清楚,
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,K,tc,ans;
int a[105],c[105],T[10005],k[105],z[105],f[105][105];
int D(int L,int R){
memset(k,0,sizeof k);
memset(z,0,sizeof z);
for(int i=1;i<=n;++i){
for(int j=1;j<=K;++j) if(c[i]+a[j]>=L) ++k[i]; else break;
for(int j=n;j>=K+1;--j) if(c[i]+a[j]<=R) ++z[i]; else break;
}
f[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=i&&j<=K;++j){
int A=j,B=i-j;
f[i][j]=0;
if(A&&k[i]>K-A) (f[i][j]+=(ll)f[i-1][j-1]*(k[i]-(K-A))%mod)%=mod;
if(B&&z[i]>B-1) (f[i][j]+=(ll)f[i-1][j]*(z[i]-(B-1))%mod)%=mod;
}
}
return f[n][K];
}
int main(){
freopen("select.in","r",stdin);
freopen("select.out","w",stdout);
scanf("%d%d",&n,&K);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i) scanf("%d",&c[i]);
sort(a+1,a+1+n,greater<int>());
sort(c+1,c+1+n,greater<int>());
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) T[++tc]=a[i]+c[j];
sort(T+1,T+1+tc);
tc=unique(T+1,T+1+tc)-(T+1);
for(int i=1;i<=tc;++i) (ans+=D(T[i],T[i])-D(T[i]+1,T[i]))%=mod;
printf("%d\n",(ans%mod+mod)%mod);
return 0;
}
跳跃
类似于ST表的预处理,lef[i][j]表示 i 跳 2^j 步能到达的左边界,rig[i][j]同理
\(lef[i][j]=min_{lef[i][j-1] \leq x \leq rig[i][j-1]} lef[x][j-1];\)
\(rig[i][j]=max_{lef[i][j-1] \leq x \leq rig[i][j-1]} rig[x][j-1];\)
预处理的时候因为要查询区间min,max所以要用ST表,其实后面倍增的时候也要查询,所以这个ST表多开一维,后面再用就不要需要预处理了,
倍增就是类似于倍增lca跳父亲,枚举当前二进制位,看答案能否加上,如果答案加上该2的次幂,仍存在两个点相互到达不了的,那么就加上,最后输出ans+1即可,这样不用二分,二分的话总复杂度 \(O(nlog^2+nlog^2)\) ,这样倍增的话优秀一点,复杂度 \(O(nlog^2+nlog)\)
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2e5+10;
int n,ans,lg[maxn];
int Max[maxn],lal[maxn],lar[maxn],vl[maxn],vr[maxn],lef[20][maxn],rig[20][maxn];
int mn[20][20][maxn],mx[20][20][maxn];
int read(int x=0,bool f=0,char ch=getchar()){
for(;ch<'0' || ch>'9';ch=getchar()) f=ch=='-';
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
return f?-x:x;
}
void Pre(int d){
for(int i=1;i<=lg[n];++i){
for(int j=1;j+(1<<i)-1<=n;++j){
mn[d][i][j]=min(mn[d][i-1][j],mn[d][i-1][j+(1<<(i-1))]);
mx[d][i][j]=max(mx[d][i-1][j],mx[d][i-1][j+(1<<(i-1))]);
}
}
}
int askmin(int x,int l,int r){
int d=lg[r-l+1];
return min(mn[x][d][l],mn[x][d][r+1-(1<<d)]);
}
int askmax(int x,int l,int r){
int d=lg[r-l+1];
return max(mx[x][d][l],mx[x][d][r+1-(1<<d)]);
}
int main(){
freopen("jump.in","r",stdin);
freopen("jump.out","w",stdout);
n=read();
for(int i=2;i<=n;++i) lg[i]=lg[i/2]+1;
for(int i=1;i<=n;++i){
int x=read();
mn[0][0][i]=lef[0][i]=max(1,i-x);
mx[0][0][i]=rig[0][i]=min(n,i+x);
}
for(int d=1;d<=lg[n];++d){
Pre(d-1);
for(int i=1;i<=n;++i){
mn[d][0][i]=lef[d][i]=askmin(d-1,lef[d-1][i],rig[d-1][i]);
mx[d][0][i]=rig[d][i]=askmax(d-1,lef[d-1][i],rig[d-1][i]);
}
}
Pre(lg[n]);
Max[n+1]=1;
for(int i=1;i<=n;++i) lal[i]=lar[i]=i;
for(int i=lg[n];i>=0;--i){
for(int x=1;x<=n;++x){
vl[x]=askmin(i,lal[x],lar[x]);
vr[x]=askmax(i,lal[x],lar[x]);
}
for(int x=n;x>=1;--x) Max[x]=max(Max[x+1],vl[x]);
bool Add=0;
for(int x=1;x<=n;++x) if(vr[x]<n&&Max[vr[x]+1]>x){Add=1;break;}
if(Add){
ans|=(1<<i);
for(int x=1;x<=n;++x) lal[x]=vl[x],lar[x]=vr[x];
}
}
printf("%d\n",ans+1);
return 0;
}
切蛋糕
计算几何直接算
Code
标签:38,20,lef,省选,int,ch,maxn,测试,include 来源: https://www.cnblogs.com/Lour688/p/14539184.html