【数学随记】浅谈牛顿恒等式及其应用
JXR_Kalcium · · 算法·理论
前言
在 蒟蒻写得烂,不喜勿喷()
牛顿恒等式(Newton's Identities)可以在 OI 中解决一类求一个序列有序/无序选取
前置知识
初等对称多项式
幂和对称多项式
导数描述的是函数在某一点的瞬时变化率(可以理解为加速度),具体地,对一个函数
生成函数是一种将序列编码为多项式系数的方法,如对于序列
牛顿恒等式及其证明
牛顿恒等式
证明
首先写出
为了与
又因为
即
然后将
所以
又因为
即
消去两边
即
得证。
拓展(线性代数与高等数学内容,可略过)
我们可以利用行列式求出
而因为
所以可通过指数函数
然后对这个指数函数进行泰勒展开,将得到的系数组成一个
即其元素
于是即有通项公式
例如当
例题
【NOIP2015 五校联考模拟赛 5 Day 1】Melancholy
形式化题意:给定
N 个数对(D_i,V_i) 和Q 次查询,每次查询给定一个查询区间[L,R] 和K ,问在所有满足D_i\in[L,R] 的V_i 中选出K 个的所有排列(满足不含这其中最小的V_i )的价值和,其中一个排列的价值定义为排列中所有数的乘积,答案对2^{32} 取模,特别地,若无解则输出0 。数据范围N,Q\le 10^5,K\le 6 ,保证所有D_i,V_i 互不相等。
解题思路
首先转换一下题意,就是要求从一个序列
而最终答案因为是有序选取,故答案
AC 代码
#include <bits/stdc++.h>
#define ll unsigned int
#define endl putchar(10)
#define R register
using namespace std;
// 快读快输
inline ll read()
{
ll x=0,f=1; char c=getchar();
while(c<48 || c>57)
{
if(c=='-') f=-1;
c=getchar();
}
while(c>47 && c<58)
x=(x<<1)+(x<<3)+c-48, c=getchar();
return x*f;
}
inline void write(ll x)
{
static ll st[41]; ll tp=0;
if(x<0) putchar('-'), x=-x;
do st[tp++]=x%10, x/=10; while(x);
while(tp) putchar(st[--tp]+48);
}
ll n,q,x,y,k,l,r,mid,f[100001][18],lg[100001],
d[100001],v[100001],sum[100001][7],s[7],mn,ans;
// 快速幂,用来预处理 s[i]
ll qpow(ll x, ll y)
{
ll res=1;
while(y)
{
if(y&1) res*=x;
x*=x; y>>=1;
}
return res;
}
// 将 D[i] 和 V[i] 按照 D[i] 为关键字从小到大排序
void qsort(ll l, ll r)
{
if(l>=r) return;
ll i=l, j=r, p=d[l+r>>1];
while(i<=j)
{
while(d[i]<p) ++i;
while(d[j]>p) --j;
if(i<=j) swap(d[i],d[j]), swap(v[i++],v[j--]);
}
qsort(l,i-1); qsort(i,r);
}
int main()
{
n=read(); q=read();
for(R ll i=2; i<=n; ++i) lg[i]=lg[i>>1]+1; // 预处理 log
for(R ll i=1; i<=n; ++i) d[i]=read();
for(R ll i=1; i<=n; ++i) v[i]=read();
qsort(1,n); // 排序
// 预处理 s[i]
for(R ll i=1; i<=n; ++i)
{
f[i][0]=v[i];
for(R ll j=1; j<7; ++j) sum[i][j]=sum[i-1][j]+qpow(v[i],j);
}
// ST 表预处理
for(R ll i=1; i<=lg[n]; ++i)
{
for(R ll j=1; j<=n-(1<<i)+1; ++j)
f[j][i]=min(f[j][i-1],f[j+(1<<i-1)][i-1]);
}
while(q--)
{
x=read(); y=read(); k=read();
l=0; r=n+1;
// 二分找到 [L,R] 对应区间
while(l<r-1)
{
mid=l+r>>1;
if(d[mid]<x) l=mid;
else r=mid;
}
x=r; l=0; r=n+1;
while(l<r-1)
{
mid=l+r>>1;
if(d[mid]>y) r=mid;
else l=mid;
}
y=l; // [L,R] 对应区间即为 [x,y]
// 判无解
if(x>y)
{
write(0); endl; continue;
}
// 找到最小值
l=lg[y-x+1]; mn=min(f[x][l],f[y-(1<<l)+1][l]);
// 重新更新 s[i]
for(R int i=1; i<=k; ++i)
s[i]=sum[y][i]-sum[x-1][i]-qpow(mn,i);
// 套公式直接计算答案
if(k<2) write(s[1]);
else if(k<3) write(s[1]*s[1]-s[2]);
else if(k<4) write(s[1]*s[1]*s[1]-s[1]*s[2]*3+s[3]*2);
else if(k<5) write(s[1]*s[1]*s[1]*s[1]-
s[1]*s[1]*s[2]*6+s[1]*s[3]*8+s[2]*s[2]*3-s[4]*6);
else if(k<6) write(s[1]*s[1]*s[1]*s[1]*s[1]-
s[1]*s[1]*s[1]*s[2]*10+s[1]*s[2]*s[2]*15+
s[1]*s[1]*s[3]*20-s[1]*s[4]*30-s[2]*s[3]*20+s[5]*24);
else write(s[1]*s[1]*s[1]*s[1]*s[1]*s[1]-
s[1]*s[1]*s[1]*s[1]*s[2]*15+s[1]*s[1]*s[2]*s[2]*45-
s[1]*s[2]*s[3]*120+s[1]*s[1]*s[1]*s[3]*40-s[1]*s[1]*s[4]*90+
s[1]*s[5]*144-s[2]*s[2]*s[2]*15+s[2]*s[4]*90+s[3]*s[3]*40-s[6]*120);
endl;
}
return 0;
}