6.15 NOI 模拟

\(T1\ ckr\)与平方数

不会吧,不会吧,真有人不会积分,好吧,我真的一点也不会。。。

基本公式\(:\)

\(1.\)多项式定积分的计算方法

\[f(x)=\sum_{i=0}^nc_ix^i \\ \int_{a}^{b}=\sum_{i=0}^n\frac{(b^{i+1}-a^{i+1})c_i}{i+1} \]

\(2.\)换元积分法

\(I\subseteq \R\)为一个区间,\(\varphi:[a,b]\rightarrow I\)是一个导数可积的函数(不是数论函数)。设\(f:I\rightarrow \R\)是一个连续函数。(说人话就是连续可导)

\[\int_a^b f(u){\rm d}u=\int_{\varphi(a)}^{\varphi(b)}f(\varphi(x))\varphi'(x){\rm d}x \]

考虑证明:

\(F'(x)=f(x)\)

\[\int f(u) {\rm d}u=F(u)+C \\ u=\varphi(x) \\ {\rm d}[F(u)]={\rm d}[F(\varphi(x))]=f(\varphi(x))\varphi'(x) {\rm d}x \\ \int f(\varphi(x))\varphi'(x) {\rm d}x=\int f(u){\rm d}u \]

\(3.\)分部积分法

\(u=u(x),{\rm d}u=u'(x){\rm d}x,v=v(x),{\rm d}v=v'(x){\rm d}x\)

\[\int_a^b u(x)v'(x){\rm d}x=[u(x)v(x)]_a^b-\int_a^b u'(x)v(x) \]

考虑证明:

\[[u(x)v(x)]'=u'(x)v(x)+u(x)v'(x) \\ [u(x)v(x)]'-u'(x)v(x)=u(x)v'(x) \]

两边分别积分即可

\[u(x)v(x)-\int u'(x)v(x)=\int u(x)v'(x) \]

然后做这道题:

算法一

假如我们只看懂了第一个式子(就像我一样)

暴力卷积求出每一项系数积回去即可

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define int long long
#define MAXN 3005
using namespace std;
const int mod=2147483647;
int a[MAXN],b[MAXN],c[MAXN<<1],fx[MAXN<<1],inv[MAXN<<1];
int N,s,t,x;
void sol(int n,int m)
{
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    a[0]=s; a[1]=1;
    b[0]=t; b[1]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=i;j>=1;j--)
        {
            a[j]=(a[j-1]+a[j]*s%mod)%mod;
        }
        (a[0]*=s)%=mod;
    }
    for(int i=2;i<=m;i++)
    {
        for(int j=i;j>=1;j--)
        {
            (b[j]=b[j-1]+b[j]*t%mod)%=mod;
        }
        (b[0]*=t)%=mod;
    }
    memset(c,0,sizeof(c));
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
           (c[i+j]+=a[i]*b[j]%mod)%=mod;
        }
    }
    int res=0;
    for(int i=0;i<=n+m;i++)
    {
        res=(res+(fx[i+1]*c[i])%mod*inv[i+1]%mod)%mod;
    }
    printf("%lld ",res);
}
void Init()
{
    fx[0]=1; fx[1]=x;
    inv[0]=inv[1]=1;
    for(int i=2;i<MAXN;i++)
    {
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        fx[i]=fx[i-1]*x%mod;
    }
}
signed main()
{
    scanf("%lld%lld%lld%lld",&N,&s,&t,&x);
    Init();
    for(int i=1;i*i<=N;i++)
    {
        for(int j=1;j*j<=N;j++)
        {
            sol(i*i,j*j);
        }
        puts("");
    }
}

算法二

使用\(FFT\)计算系数,分数不变

算法三

\(s=t\),可以直接套用公式

\[\int (x+t)^n {\rm d}x=\frac{(x+t)^{n+1}}{n+1}+C \]

算法四

我们看懂了第二条和第三条公式,但是我们不想往下推了!

\(x\leftarrow x+s\)

\[\int_0^{x_0}(x+s)^n(x+t)^m {\rm d}x=\int_s^{x_0+s}x^n(x+t-s)^m{\rm d}x \]

考虑对上面这个东西分步积分

\[\int_s^{x_0+s}x^n(x+t-s)^m{\rm d}x=[\frac{x^{n+1}}{n+1}(x+t-s)^m]_{s}^{x_0+s}-\int_s^{x_0+s}\frac{x^{n+1}}{n+1}m(x+t-s)^{m-1} \]

后面的这个式子可以递归,可以做到\(O(n+m)\)

算法EX

这时候,你想\(A\)掉这道题

#include<bits/stdc++.h>
#define mod 2147483647
#define int long long 
#define MAXN 200002
using namespace std;
int N,s,t,x0;
int pt[MAXN],fac[MAXN],inv[MAXN],invs[MAXN],pows[MAXN],powt[MAXN],spow[MAXN],tpow[MAXN];
int my_pow(int x,int y)
{
    int res=1;
    while(y)
    {
        if(y&1) res=res*x%mod;
        x=x*x%mod;
        y=(y>>1);
    }
    return res;
}
int Inv(int x)
{
    return my_pow(x,mod-2);
}
void Init()
{
	fac[0]=1;
    for(int i=1;i<MAXN;++i) fac[i]=fac[i-1]*i%mod;
    inv[MAXN-1]=Inv(fac[MAXN-1]);
    pows[0]=powt[0]=spow[0]=tpow[0]=1;
    for(int i=MAXN-2;i>=0;--i) inv[i]=inv[i+1]*(i+1)%mod;
    int tmp=s-t;
    tmp+=(tmp>>31)&mod;
    for(int i=1;i<MAXN;++i)
    {
        pows[i]=pows[i-1]*(x0+s)%mod;
        powt[i]=powt[i-1]*(x0+t)%mod;
        spow[i]=spow[i-1]*s%mod;
        tpow[i]=tpow[i-1]*t%mod;
        invs[i]=inv[i]*fac[i-1]%mod;
        pt[i]=pt[i-1]+tmp-mod;pt[i]+=pt[i]>>31&mod;
    }
}
signed main()
{
    scanf("%lld %lld %lld %lld",&N,&s,&t,&x0);
    Init();
    for(int i=1;i*i<=N;++i)
    {
        int res=(pows[i*i+1]-spow[i*i+1])*invs[i*i+1]%mod;
        for(int j=1;j<=N;++j)
        {
            (res=pows[i*i+1]*powt[j]%mod+pt[j]*(mod-res)%mod)%=mod;
            (res+=(mod-spow[i*i+1])*tpow[j])%=mod;
            res%=mod;
            res+=res>>63&mod;res+=res>>63&mod;
            (res*=invs[i*i+j+1])%=mod;
            if((int)sqrt(j)*(int)sqrt(j)==j)
            cout<<(res%mod+mod)%mod<<" ";
        }
        cout<<endl;
    }
}

\(T2\ math\)

考虑一下单位根的性质

\[\sum_{i=0}^{n-1}w_n^{i\times k}= \left\{ \begin{array}{**lr**} n\ \ \ \ n \mid k \\ 0\ \ \ \ n\nmid k& \end{array} \right. \]

有多项式

\[f(x)=a_0+a_1 x+a_2 x^2...+a_K x^K \]

\(x^n=1\)的根\(r\)全部带进去,\(\sum f(r)\)

\[\sum_{i=1}^n f(r_i)=\sum_{i=0}^{n-1}f(w_n^i)=\sum_{i=0}^{n-1}\sum_{j=0}^Ka_jw_n^{ij}=\sum_{j=0}^Ka_j\sum_{i=0}^{n-1}w_n^{ij}=n\times\sum_{j=0}^{\lfloor\frac{K}{n}\rfloor}a_{in} \]

我们要求所有的不超过\(n\)次的单位根,重复的只求一次的\(\sum f(r)\)

设本原单位根为\(w_n^k,\gcd(n,k)=1\)

\(F(i)=\sum f(r)[r\)\(i\)次本原单位根\(]\)

\(G(i)=\sum f(r)[r\)\(i\)次单位根\(]\)

\[\sum_{d\mid n}F(d)=G(n) \]

考虑证明:

我们每个单位根第一次出现肯定在因数的位置,感觉这个东西和\(\sum_{d|n}\varphi(d)=n\)有点像

\[F*I=G \\ F=G/I \]

考虑用杜教筛解决

#include<bits/stdc++.h>
//#define int long long 
#define ll long long 
#define rint register int 
#define LL __int128
#define N 10000010
using namespace std;
char buf[1<<21],*p1,*p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int n,k,a[N],tot;
ll idn[N],IDN[N];
ll ID(ll x)
{
    if(x<=n/x) return idn[x]=idn[x]?idn[x]:++tot;
    else return IDN[n/x]=IDN[n/x]?IDN[n/x]:++tot;
}
bitset<N> sh;
ll prime[N],phi[N],qzp[N],f[N],mu[N],qzm[N];
int cnt;
ll phis[N],mus[N];
ll Phi(ll x)
{
    if(x<=N-10) return qzp[x];
    if(phis[ID(x)]) return phis[ID(x)];
    ll ans=(x+1)*x/2;
    for(ll l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        ans-=(r-l+1)*Phi(x/l);
    }
    return phis[ID(x)]=ans;
}
int Mu(rint x)
{
    if(x<=N-10) return qzm[x];
    if(mus[ID(x)]) return mus[ID(x)];
    rint ans=1;
    for(rint l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        ans-=(r-l+1)*Mu(x/l);
    }
    return mus[ID(x)]=ans;
}
void write(LL x)
{
    if(x/10) write(x/10);
    putchar(x%10+'0');
}
inline int read()
{
    rint x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
signed main()
{
    n=read(),k=read();
    qzp[1]=phi[1]=mu[1]=qzm[1]=1;
    for(ll i=2;i<=N-10;++i)
    {
        if(!sh[i])
        {
            prime[++cnt]=i;
            phi[i]=i-1;
            mu[i]=-1;
        }
        for(rint j=1;j<=cnt&&prime[j]*i<N;++j)
        {
            sh[i*prime[j]]=1;
            if(i%prime[j])
            {
                phi[i*prime[j]]=phi[i]*phi[prime[j]];
                mu[i*prime[j]]=-mu[i];
            }
            else
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                mu[i*prime[j]]=0;
                break;
            }
        }
    }
    for(rint i=2;i<=N-10;++i) qzp[i]=qzp[i-1]+phi[i],qzm[i]=qzm[i-1]+mu[i];
    for(rint i=0;i<=k;++i) a[i]=read();
    LL ans=(LL)a[0]*Phi(n);
    for(rint i=1;i<=k;++i) f[i]=(LL)i*Mu(n/i);
    for(rint i=1;i<=cnt&&prime[i]<=k;++i)
    for(rint j=1;j<=k/prime[i];++j)
    f[j*prime[i]]+=f[j];
    for(rint i=1;i<=k;++i) ans+=(LL)f[i]*a[i];
    if(ans<0) ans=-ans,putchar('-');
    write(ans);
}

\(T3\ viru\)

暴力可以拿\(90pts\)

考虑对于每种颜色,求哪些矩阵不能包含。

考虑如何\(O(n^3)\)

首先根号分治,出现次数小的直接暴力,出现次数大的考虑扫一遍

扫的话,考虑枚举下边界,左右边界,我们需要求的是,每一列最靠下的位置在哪。

可以拿到\(90pts\)(粘一下其他老哥代码)

#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
const int N=1510;
int n,m,w[N],h[N],up[N][N];
bool a[N][N];stack<int> s;
vector<int> v[N*N];
long long ans;
void calc1(int p){
    sort(v[p].begin(),v[p].end());
    for(int i=0;i<v[p].size();i++){
        int x=v[p][i]/n,y=v[p][i]%n;
        int l=0,r=n-1,last=x;
        for(int j=i+1;j<v[p].size();j++){
            int _x=v[p][j]/n,_y=v[p][j]%n;
            ans+=1ll*(_x-last)*(r-y+1)*(y-l+1)*(x+1);
            if(_y<=y)l=max(_y+1,l);
            if(_y>=y)r=min(_y-1,r);
            last=_x;
        }
        ans+=1ll*(n-last)*(r-y+1)*(y-l+1)*(x+1);
    }
    return;
}
void calc2(int p){
    for(int i=0;i<v[p].size();i++)
        a[v[p][i]/n][v[p][i]%n]=1;
    ans+=1ll*n*n*(n+1)*(n+1)/4;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(a[i][j])up[i][j]=i+1;
            else if(i)up[i][j]=up[i-1][j];
            else up[i][j]=0;
            h[j]=i-up[i][j]+1;w[j]=1;
            while(!s.empty()&&h[j]<=h[s.top()]){
                ans-=1ll*h[s.top()]*w[s.top()]*w[j];
                w[j]+=w[s.top()];s.pop();
            }
            s.push(j);
        }
        int len=1;
        while(!s.empty())
            ans-=1ll*h[s.top()]*w[s.top()]*len,len+=w[s.top()],s.pop();
    }
    for(int i=0;i<v[p].size();i++)
        a[v[p][i]/n][v[p][i]%n]=0;
    return;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        for(int j=0,x;j<n;j++){
            scanf("%d",&x);
//          x=rand()%m;
            v[x].push_back(i*n+j);
        }
    for(int i=1;i<=n*n;i++)
        if(v[i].size()<=n)calc1(i);
        else calc2(i);
    printf("%lld\n",ans);
    return 0;

正解的话需要笛卡尔树

#include<bits/stdc++.h>
#define ls t[x].l
#define rs t[x].r
using namespace std;
struct node{
    int fa,l,r,L,R,val,s;
    void init()
    {
        fa=l=r=L=R=val=s=0;
    }
}t[2250011];
int n,cnt,sum,rt,la;
int col[2250011],id[2250011],x[2250011],y[2250011];
int exis[2250011],p[2250011],g[2250011];
long long ans;
bool cmp(int a,int b)
{
    if(col[a]==col[b]) return a<b;
    return col[a]<col[b];
}
void push_up(int x)
{
    t[x].L=ls?t[ls].L:p[x-1];
    t[x].R=rs?t[rs].R:p[x+1];
    t[x].s=t[ls].s+t[rs].s+t[x].val*(p[x]-t[x].L)*(t[x].R-p[x]);
}
int build(int f,int l,int r)
{
    if(l>r)return 0;
    int mid=(l+r)>>1;
    t[mid].fa=f,t[mid].L=p[l-1],t[mid].R=p[r+1];
    t[mid].l=build(mid,l,mid-1),t[mid].r=build(mid,mid+1,r);
    return mid;
}
void re(int x)
{
    int f=t[x].fa,gf=t[f].fa;
    if(gf)
    {
        if(t[gf].l==f) t[gf].l=x;
        if(t[gf].r==f) t[gf].r=x;
    }
    if(t[f].l==x) t[rs].fa=f,t[f].l=rs,rs=f;
    else t[ls].fa=f,t[f].r=ls,ls=f;
    t[f].fa=x,t[x].fa=gf;
    push_up(f);
}
void tar(int x,int c)
{
    t[x].val=c;
    int f=0;
    while(f=t[x].fa)
    {
        if(t[f].val>=t[x].val) break;
        re(x);
    }
    while(1)
    {
        push_up(x);
        if(!t[x].fa) return rt=x,void();
        x=t[x].fa;
    }
}
void gets(int l,int r)
{
    cnt=sum=0;
    for(int i=l;i<=r;++i)
    {
        cnt++;
        x[cnt]=((id[i]-1)/n)+1;
        y[cnt]=((id[i]-1)%n)+1;
        if(!exis[y[cnt]]) exis[y[cnt]]=1,p[++sum]=y[cnt];
    }
    p[0]=0,p[sum+1]=n+1;
    sort(p+1,p+sum+1);
}
void chu_ans(int l,int r)
{
    gets(l,r);
    for(int i=1;i<=sum;++i) t[i].init();
    for(int i=1;i<=sum;++i) g[p[i]]=i;
    rt=build(0,1,sum),la=0;
    for(int i=1;i<=cnt;++i)
    {
        ans=ans+1ll*t[rt].s*(x[i]-1-la);
        la=x[i]-1;
        tar(g[y[i]],x[i]);
    }
    ans=ans+1ll*t[rt].s*(n-la);
    for(int i=1;i<=sum;++i) exis[p[i]]=0;
}
//char buf[1<<21],*p1,*p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
int main()
{
//  freopen("1.in","r",stdin);
//  freopen("1.out","w",stdout);
    n=read();
    for(int i=1;i<=n*n;++i) col[i]=read();
    int all_the_same=col[1];
    for(int i=1;i<=n*n;++i) if(col[i]!=all_the_same) all_the_same=0;
//  cerr<<"ww";
    if(all_the_same) cout<<1ll*n*(n+1)*n*(n+1)/4,exit(0);
    for(int i=1;i<=n*n;++i) id[i]=i;
    sort(id+1,id+n*n+1,cmp);
    for(int l=1,r;l<=n*n;l=r+1)
    {
        r=l;
        while(r<n*n&&col[id[r+1]]==col[id[l]]) ++r;
        chu_ans(l,r);
    }
    cout<<ans;
}
posted @ 2022-06-16 15:16  Authentic_k  阅读(83)  评论(0编辑  收藏  举报