1 ≤ n ≤ 500 , 0 ≤ k ≤ 50 1\leq n\leq 500,0\leq k\leq 50 1≤n≤500,0≤k≤50
设 f ( i , j , k ) f(i,j,k) f(i,j,k)表示 k k k次操作后 P i < P j P_i<P_j Pi<Pj的概率。
考虑暴力 O ( n 4 k ) O(n^4k) O(n4k)的转移:
前缀和的前缀和将复杂度优化到 O ( n 2 k ) O(n^2k) O(n2k)(为简洁表达,忽略 k k k一维):
如上维护即可。
#include
#define rep(i,x,y) for(i=x;i<=y;++i)
#define req(i,x,y) for(i=x;i>=y;--i)
#define gc getchar
using namespace std;
typedef long long ll;
typedef double db;
const int N=505,mod=1e9+7;
int n,m,ini[N],f[N][N],ss[N],nv;
int a[N][N],b[N][N],c[N][N];
ll ans;
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
inline int adi(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dci(int x,int y){x-=y;return x<0?x+mod:x;}
char cp,os[100];
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) re=(ll)re*x%mod;
return re;
}
int main(){
int i,j,k,v;rd(n);rd(m);
rep(i,1,n) rd(ini[i]),ss[i]=ss[i-1]+i;
rep(i,1,n) rep(j,i+1,n) f[i][j]=(ini[i]>ini[j]);
nv=fp(ss[n],mod-2);
for(;m;--m){
rep(j,2,n){
rep(i,1,j-1) a[i][j]=adi(a[i-1][j],f[i][j]);
rep(i,1,j-1) ad(a[i][j],a[i-1][j]);
}
rep(i,1,n){
req(j,n,i+1) b[i][j]=adi(b[i][j+1],f[i][j]);
req(j,n,i+1) ad(b[i][j],b[i][j+1]);
}
rep(j,0,n){
rep(i,1,n-j) c[i][j]=adi(c[i-1][j],f[i][i+j]);
rep(i,1,n-j) ad(c[i][j],c[i-1][j]);
}
rep(i,1,n)
rep(j,i+1,n){
v=(ll)f[i][j]*(ss[i-1]+ss[j-i-1]+ss[n-j])%mod;
ad(v,dci(a[j-1][j],adi(a[j-i-1][j],a[i-1][j])));
ad(v,dci(b[i][i+1],adi(b[i][j+1],b[i][n+i-j+2])));
dc(v,dci(c[n+i-j][j-i],adi(c[n-j][j-i],c[i-1][j-i])));
ad(v,i*(n-j+1));
f[i][j]=(ll)v*nv%mod;
}
}
rep(i,1,n) rep(j,i+1,n) ans+=f[i][j];
printf("%d",ans%mod);
fclose(stdin);fclose(stdout);
return 0;
}
n ≤ 1 0 5 , ∣ A i ∣ ≤ 1 0 7 n\leq 10^5,|A_i|\leq 10^7 n≤105,∣Ai∣≤107
设 f ( i , j ) f(i,j) f(i,j)表示前 i i i个数选了 j j j个数的最大权值,则
f ( i , j ) = m a x ( f ( i − 1 , j ) , f ( i − 1 , j − 1 ) + j × A i ) f(i,j)=max(f(i-1,j),f(i-1,j-1)+j\times A_i) f(i,j)=max(f(i−1,j),f(i−1,j−1)+j×Ai)
打表/感性/证明发现:对于每一个 i i i,满足 f ( i , j ) f(i,j) f(i,j)最优的 j j j的范围一定是一端以 i i i的后缀。考虑 b s t bst bst维护 d p dp dp值的差分,即 f ( i , j ) − f ( i , j − 1 ) f(i,j)-f(i,j-1) f(i,j)−f(i,j−1)。
每次新加入末端的一个数 a i a_i ai,首先二分找出其在 b s t bst bst上所处位置,然后将后面所有点打上 + a i +a_i +ai标记即可。
#include
#define lc(x) t[x].ch[0]
#define rc(x) t[x].ch[1]
#define F(x) t[x].fa
using namespace std;
const int N=1e5+10;
typedef long long ll;
int n,a[N],rt,num,stk[N],top;
ll ans[N];
char cp;
inline void rd(int &x)
{
cp=getchar();x=0;int f=0;
for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
if(f) x=-x;
}
struct node{int ch[2],fa,sz;ll g,lzy;}t[N];
inline void pu(int x)
{if(!x) return;t[x].sz=t[lc(x)].sz+t[rc(x)].sz+1;}
inline void dn(int x)
{
if(!t[x].lzy) return;
if(lc(x)){
t[lc(x)].lzy+=t[x].lzy;
t[lc(x)].g+=t[x].lzy;
}
if(rc(x)){
t[rc(x)].lzy+=t[x].lzy;
t[rc(x)].g+=t[x].lzy;
}
t[x].lzy=0LL;
}
void ins(int fr,int &x,int id,int v,int bs)
{
if(!x) {x=id;F(x)=fr;t[x].g=(ll)(bs+1)*v;return;}
dn(x);
if((ll)(bs+t[lc(x)].sz+1)*v>t[x].g) ins(x,lc(x),id,v,bs);
else ins(x,rc(x),id,v,bs+t[lc(x)].sz+1);
pu(x);
}
void dfs(int x)
{
dn(x);
if(lc(x)) dfs(lc(x));
num++;
ans[num]=t[x].g;
if(rc(x)) dfs(rc(x));
}
inline void rot(int x)
{
int y=F(x),z=F(y),dr=(rc(y)==x);
t[y].ch[dr]=t[x].ch[dr^1];
if(t[y].ch[dr]) F(t[y].ch[dr])=y;
F(x)=z;if(z) t[z].ch[rc(z)==y]=x;
F(y)=x;t[x].ch[dr^1]=y;pu(y);
}
inline void splay(int x)
{
int y,z;
for(y=x;y;y=F(y)) stk[++top]=y;
for(;top;--top) dn(stk[top]);
for(;F(x);rot(x)){
y=F(x);z=F(y);
if(z) ((rc(y)==x)^(rc(z)==y))?rot(x):rot(y);
}
rt=x;pu(x);
}
int main(){
int i,j;rd(n);
for(i=1;i<=n;++i) rd(a[i]),t[i].sz=1;
rt=1;t[1].g=a[1];
for(i=2;i<=n;++i){
ins(0,rt,i,a[i],0);splay(i);
if(rc(i)){t[rc(i)].lzy+=a[i];t[rc(i)].g+=a[i];}
}
dfs(rt);
for(i=2;i<=num;++i) ans[i]+=ans[i-1];
for(i=1;i<=num;++i) printf("%lld ",ans[i]);
return 0;
}
4 ≤ n ≤ 2 × 1 0 6 , ∣ x i ∣ , ∣ y i ∣ ≤ 1 0 9 4\leq n\leq2\times 10^6,|x_i|,|y_i|\leq 10^9 4≤n≤2×106,∣xi∣,∣yi∣≤109
两半的面积之差可以转成 ∣ S − 2 X ∣ |S-2X| ∣S−2X∣,其中 S S S为总面积, X X X为其中一半的面积。
枚举对角线,考虑固定对角线的一端,其对踵点前的一段区间均满足 2 X ≤ S 2X\leq S 2X≤S,后一段均满足 2 X > S 2X>S 2X>S,拆绝对值后转成了求边叉积(相当于一些以原点为端点的三角形的有向面积)的前缀和。再维护一个端点横纵坐标的前缀和,大力讨论即可。
复杂度 O ( n ) O(n) O(n)(线性求对踵点,代码二分求的对踵点( O ( n log n ) O(n\log n) O(nlogn)也可过)
#include
using namespace std;
typedef long long ll;
const int N=4e6+10,mod=1e9+7;
int n,ans,dz[N],pz[N],ss;
ll sum,qz[N];
struct P{
ll x,y;
P(ll x_=0,ll y_=0):x(x_),y(y_){};
P operator -(const P&ky){return P(x-ky.x,y-ky.y);}
P operator +(const P&ky){return P(x+ky.x,y+ky.y);}
}p[N],s[N];
inline ll cg(P a,P b){return a.x*b.y-a.y*b.x;}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) re=(ll)re*x%mod;
return re;
}
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
inline int adi(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dci(int x,int y){x-=y;return x<0?x+mod:x;}
inline char gc()
{
static char buf[1000005];static int p1=0,p2=0;
if(p1==p2) p1=0,p2=fread(buf,1,1000005,stdin);
if(p1==p2) return EOF;return buf[p1++];
}
char cp,os[100];
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
template<class T>inline void ot(T x)
{
if(x<0) putchar('-'),x=-x;
int re=0;
for(;(!re)||x;x/=10) os[++re]='0'+x%10;
for(;re;--re) putchar(os[re]);
}
inline int zlh(ll x){x%=mod;return x<0?x+mod:x;}
inline ll askk(int l,int r){
if(r<=n) return qz[r]-qz[l-1];
return sum-(qz[l-1]-qz[r-n]);
}
inline ll gtarea(int l,int r){return askk(l,r-1)+cg(p[l],p[r]);}
inline void sol(int id)
{
int l=id+2,r=id-2+n,mid,re=id,bs,res,o=0;
for(;l<=r;){
mid=(l+r)>>1;
((gtarea(id,mid))<=(sum>>1))?l=(re=mid)+1:r=mid-1;
}
if(re!=id){
bs=n-3-((re-id-1)<<1);ad(ans,zlh(-(ll)bs*ss));
res=dci(dz[re-1],dz[id]);
ad(res,zlh((ll)(bs-1-id)*dci(pz[re-1],pz[id])));
ad(res,zlh((ll)bs*dci(pz[id],pz[id-1])));
ad(o,res);
res=dci((ll)zlh(p[id].x)*dci(s[re].y,s[id+1].y)%mod,(ll)zlh(p[id].y)*dci(s[re].x,s[id+1].x)%mod);
dc(o,res);
if(re<id-2+n){
bs=id-2+n-re;
res=dci(dz[id-3+n],dz[re-1]);
ad(res,zlh((ll)(-re-bs)*dci(pz[id-3+n],pz[re-1])));
dc(o,res);
res=dci((ll)zlh(p[id].x)*dci(s[id-2+n].y,s[re].y)%mod,(ll)zlh(p[id].y)*dci(s[id-2+n].x,s[re].x)%mod);
ad(o,res);
}
}else{
dc(ans,(ll)(n-3)*ss%mod);
bs=id-2+n;res=dci(dz[bs-1],dz[id]);
ad(res,zlh((ll)(-n+3-id-1)*dci(pz[bs-1],pz[id])));
ad(res,zlh((ll)(-n+3)*dci(pz[id],pz[id-1])));
dc(o,res);
res=dci((ll)zlh(p[id].x)*dci(s[bs].y,s[id+1].y)%mod,(ll)zlh(p[id].y)*dci(s[bs].x,s[id+1].x)%mod);
ad(o,res);
}
ad(ans,adi(o,o));
}
int main(){
int i;ll res;rd(n);qz[0]=0LL;
for(i=1;i<=n;++i) rd(p[i].x),rd(p[i].y);
for(i=2;i<=n;++i) p[i]=p[i]-p[1];p[1]=P(0LL,0LL);
for(i=1;i<=n;++i) p[i+n]=p[i];
for(i=1;i<=n;++i) qz[i]=qz[i-1]+cg(p[i+1],p[i]);
for(i=1;i<n+n;++i){
s[i]=s[i-1]+p[i];s[i].x=zlh(s[i].x);s[i].y=zlh(s[i].y);
pz[i]=adi(pz[i-1],zlh(cg(p[i+1],p[i])));
}
sum=qz[n];ss=pz[n];
for(i=1;i<n+n;++i) dz[i]=adi(dz[i-1],(ll)i*dci(pz[i],pz[i-1])%mod);
for(i=1;i<=n;++i) sol(i);
printf("%d",(ll)ans*fp(2,mod-2)%mod);
fclose(stdin);fclose(stdout);
return 0;
}
小结
T1连dp转移都没想出来,更别说后面的繁琐讨论+求前缀和的前缀和的优化了。
T2比较套路,但还是没有联想到最优决策是一段后缀就上 b s t bst bst。
T3很傻,但还是码了很久。