bzoj4543原题
题解
n , m ≤ 1 0 5 , A i ≤ 1 0 8 n,m\leq 10^5,A_i\leq 10^8 n,m≤105,Ai≤108
一道比较巧妙的题:
首先将 A A A按升序排序,最优的答案就是 ∑ i = 1 n ( n − i + 1 ) × A i \sum\limits_{i=1}^n (n-i+1)\times A_i i=1∑n(n−i+1)×Ai
每个操作后的排列可以用一系列二元组 ( u , v ) (u,v) (u,v)表达出来(且这个表达是唯一的)(满足 u u u单调递增):
( u , v ) (u,v) (u,v)表示将排列 [ u − v , u ] [u-v,u] [u−v,u]循环右移一位,其代价 g ( u , v ) = ∑ i = 1 v A u − A u − i g(u,v)=\sum\limits_{i=1}^vA_u-A_{u-i} g(u,v)=i=1∑vAu−Au−i
显然 g ( u , v ) ≤ g ( u , v + 1 ) g(u,v)\leq g(u,v+1) g(u,v)≤g(u,v+1),由增量算法向外拓展 k − 1 k-1 k−1步就得到了前 k k k小的值。
考虑主席树维护 g ( u , v ) ( 1 ≤ u ≤ n ) g(u,v)(1\leq u\leq n) g(u,v)(1≤u≤n),初始化所有 v = 1 v=1 v=1,每次取最小的 g g g拓展。
拓展后可以得到新的两种状态:
时间复杂度 O ( ( n + k ) log n ) O((n+k)\log n) O((n+k)logn)
代码比较有技巧性:
#include
#define mid (l+r>>1)
#define lc t[k].ls
#define rc t[k].rs
using namespace std;
const int N=1e5+10,M=2e7+10;
typedef long long ll;
const ll inf=1e18;
int n,m,a[N],num,cnt;
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;
}
template<class T>void prit(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>=10) prit(x/10);
putchar('0'+x%10);
}
struct P{int tg;ll v;bool operator<(const P&ky)const{return ky.v<v;};}tp;
priority_queue<P>que;
struct node{int ls,rs,u,v,s;ll g;}t[M];
struct Q{int nw,ori;ll ss;}q[N];
inline void build(int &k,int l,int r)
{
t[(k=++cnt)].s=r-l+1;
if(l==r) return;
build(lc,l,mid);build(rc,mid+1,r);
}
inline void pu(int k)
{
if(t[lc].g<t[rc].g)
t[k].u=t[lc].u,t[k].v=t[lc].v,t[k].g=t[lc].g;
else t[k].u=t[rc].u+t[lc].s,t[k].v=t[rc].v,t[k].g=t[rc].g;
t[k].s=t[lc].s+t[rc].s;
}
void ins(int &k,int pos,ll v)
{
t[++cnt]=t[k];k=cnt;
if((!lc)&&(!rc)){
t[k].u=t[k].s=1;
t[k].v++;t[k].g+=v;
return;
}
t[lc].s>=pos?ins(lc,pos,v):ins(rc,pos-t[lc].s,v);
pu(k);
}
void chg(int &k,int pos,ll v,int exi)
{
t[++cnt]=t[k];k=cnt;
if((!lc)&&(!rc)){t[k].g=v;t[k].u=t[k].s=exi;return;}
t[lc].s>=pos?chg(lc,pos,v,exi):chg(rc,pos-t[lc].s,v,exi);
pu(k);
}
void del(int &k,int pos)
{
if(!pos) return;
t[++cnt]=t[k];k=cnt;
if(t[lc].s>pos) del(lc,pos);
else{pos-=t[lc].s;lc=0;del(rc,pos);}
pu(k);
}
int ask(int k,int l,int r,int pos)
{
if(l==r) return a[l];
return t[lc].s>=pos?ask(lc,l,mid,pos):ask(rc,mid+1,r,pos-t[lc].s);
}
inline void upp(int cs)
{
int x=q[cs].nw,u=t[x].u,v=t[x].v;
q[++num].ss=q[cs].ss+t[x].g;
q[num].nw=q[cs].ori;
chg(q[num].nw,u,inf,0);
if(u-v>1) del(q[num].nw,u-v-1);
if(v<t[q[num].nw].s) chg(q[num].nw,v+1,ask(q[num].nw,1,n,v+1)-ask(q[num].nw,1,n,v),1);
chg(q[num].nw,1,inf,1);q[num].ori=q[num].nw;
que.push((P){num,q[num].ss+t[q[num].nw].g});
if(u>v+1) ins(q[cs].nw,u,ask(q[cs].nw,1,n,u)-ask(q[cs].nw,1,n,u-v-1));
else ins(q[cs].nw,u,inf);
que.push((P){cs,q[cs].ss+t[q[cs].nw].g});
}
int main(){
int i,j;
rd(n);rd(m);t[0].g=inf;
for(i=1;i<=n;++i) rd(a[i]);
sort(a+1,a+n+1);
build(q[0].nw,1,n);
for(i=1;i<=n;++i){
q[0].ss+=(ll)(n-i+1)*a[i];
i>1?ins(q[0].nw,i,a[i]-a[i-1]):ins(q[0].nw,1,inf);
}
q[0].ori=q[0].nw;
prit(q[0].ss);putchar('\n');
que.push((P){0,q[0].ss+t[q[0].nw].g});
for(m--;m;--m){
tp=que.top();que.pop();
prit(tp.v);putchar('\n');
upp(tp.tg);
}
return 0;
}
T ≤ 10 , n ≤ 1 0 9 , m ≤ 30 T\leq 10,n\leq 10^9,m\leq 30 T≤10,n≤109,m≤30
三角函数的巧妙运用(有一种化简三角函数的感觉(雾
设 f ( n , m ) = ∑ ( ∑ i = 1 m k i ) = n ∏ i = 1 m sin ( k i ⋅ x ) f(n,m)=\sum\limits_{(\sum\limits_{i=1}^mk_i)=n}\prod\limits_{i=1}^m\sin(k_i·x) f(n,m)=(i=1∑mki)=n∑i=1∏msin(ki⋅x)。
k = 1 k=1 k=1时,
f ( n − 1 , m − 1 ) × sin ( x ) → f ( n , m ) f(n-1,m-1)\times\sin(x)\rightarrow f(n,m) f(n−1,m−1)×sin(x)→f(n,m)。
k > 1 k>1 k>1时,
f ( n − k , m − 1 ) × sin ( k x ) → f ( n , m ) f(n-k,m-1)\times \sin(kx)\rightarrow f(n,m) f(n−k,m−1)×sin(kx)→f(n,m)
sin ( k x ) = sin ( x + ( k − 1 ) x ) = sin ( x ) cos ( ( k − 1 ) x ) + cos ( x ) sin ( ( k − 1 ) x ) \sin(kx)=\sin(x+(k-1)x)=\sin(x)\cos((k-1)x)+\cos(x)\sin((k-1)x) sin(kx)=sin(x+(k−1)x)=sin(x)cos((k−1)x)+cos(x)sin((k−1)x)
由
sin ( x ) cos ( k x ) = sin ( x ) ( cos ( x ) cos ( ( k − 1 ) x ) − sin ( x ) sin ( ( k − 1 ) x ) ) \sin(x)\cos(kx)=\sin(x)(\cos(x)\cos((k-1)x)-\sin(x)\sin((k-1)x)) sin(x)cos(kx)=sin(x)(cos(x)cos((k−1)x)−sin(x)sin((k−1)x))
sin ( x ) cos ( x ) cos ( ( k − 1 ) x ) + ( cos 2 ( x ) − 1 ) sin ( ( k − 1 ) x ) \qquad \qquad \qquad \quad \ \ \sin(x)\cos(x)\cos((k-1)x)+(\cos^2(x)-1)\sin((k-1)x) sin(x)cos(x)cos((k−1)x)+(cos2(x)−1)sin((k−1)x)
cos ( x ) ( sin ( x ) cos ( ( k − 1 ) x ) + cos ( x ) sin ( ( k − 1 ) x ) ) − sin ( ( k − 1 ) x ) \qquad \qquad \qquad \quad \ \ \cos(x)(\sin(x)\cos((k-1)x)+\cos(x)\sin((k-1)x))-\sin((k-1)x) cos(x)(sin(x)cos((k−1)x)+cos(x)sin((k−1)x))−sin((k−1)x)
cos ( x ) sin ( k x ) − sin ( ( k − 1 ) x ) \qquad \qquad \qquad \quad \ \ \cos(x)\sin(kx)-\sin((k-1)x) cos(x)sin(kx)−sin((k−1)x)
得
sin ( k x ) = 2 cos ( x ) sin ( ( k − 1 ) x ) − sin ( ( k − 2 ) x ) \sin(kx)=2\cos(x)\sin((k-1)x)-\sin((k-2)x) sin(kx)=2cos(x)sin((k−1)x)−sin((k−2)x)
即 2 cos ( x ) f ( n − 1 , m ) − f ( n − 2 , m ) → f ( n , m ) 2\cos(x)f(n-1,m)-f(n-2,m)\rightarrow f(n,m) 2cos(x)f(n−1,m)−f(n−2,m)→f(n,m)
m m m这维很小可以进矩阵,所以按 n n n转移,转移方程:
f [ n ] [ m ] = 2 cos ( x ) f [ n − 1 ] [ m ] − f [ n − 2 ] [ m ] + sin ( x ) f [ n − 1 ] [ m − 1 ] f[n][m]=2\cos(x)f[n-1][m]-f[n-2][m]+\sin(x)f[n-1][m-1] f[n][m]=2cos(x)f[n−1][m]−f[n−2][m]+sin(x)f[n−1][m−1]
矩乘优化后时间复杂度 O ( m 3 log n ) O(m^3\log n) O(m3logn)
#include
using namespace std;
const int N=62;
typedef double db;
int tk,n,m,bs;db x;
struct mat{
db a[N][N];
mat operator *(const mat&ky){
mat re;int i,j,k;db res;
for(i=1;i<=bs;++i)
for(j=1;j<=bs;++j){
for(res=0,k=1;k<=bs;++k) res+=a[i][k]*ky.a[k][j];
re.a[i][j]=res;
}
return re;
}
}A,B;
int main(){
int i,j;db r,t;
for(scanf("%d",&tk);tk;--tk){
scanf("%d%d%lf",&m,&n,&x);bs=m<<1;
memset(A.a,0,sizeof(A.a));memset(B.a,0,sizeof(B.a));
r=2*cos(x);t=sin(x);
for(i=m+1;i<=bs;++i){
A.a[i-m][i]=-1;A.a[i][i-m]=1;A.a[i][i]=r;
if(i<bs) A.a[i][i+1]=t;
}
B.a[1][1]=t;B.a[1][m+1]=sin(2*x);B.a[1][m+2]=t*t;
for(n--;n;n>>=1,A=A*A) if(n&1) B=B*A;
r=B.a[1][m];
if(r>0) putchar('+');else r=-r,putchar('-');
for(;r>=10.0;r/=10);for(;r<1;r*=10);
printf("%d\n",(int)r);
}
return 0;
}
小结
全程打暴力。
T1经典dp&原题竟然没有做出来。
T2太巧妙了orz只会打暴力。
T3一直都没读懂题意。