In mathematics you don't understand things, you just get used to them.

分类 笔记 下的文章

Description

Link.

给出一个长度为 $n$ 的序列 $a$,问有序三元组 $(a_{i},a_{j},a_{k})$ 使得 $i\neq j\neq k$ 且 $a_{i}+a_{j}=a_{k}$ 的数量。

Solution

发现这个值域有说头,于是设 $F(x)=\sum_{i=1}^{n}x^{a_{i}}$。

然后求出 $F^{2}(x)$,判断一通就好了。

主要是记录一下这种多项式题的常用 trick,把值域小的整成多项式或者生成函数一类的。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
namespace Poly
{
    typedef complex<double> comp;
    typedef vector<complex<double> > poly;
    #define len(x) (LL((x).size()))
    const double bh_pi=acos(-1);
    LL lim,rev[400010];
    void fft(poly &f,LL op)
    {
        for(LL i=0;i<lim;++i)    if(i<rev[i])    swap(f[i],f[rev[i]]);
        for(LL len=2;len<=lim;len<<=1)
        {
            comp bas(cos(2*bh_pi/len),op*sin(2*bh_pi/len));
            for(LL fr=0;fr<lim;fr+=len)
            {
                comp now(1,0);
                for(LL ba=fr;ba<fr+(len>>1);++ba,now*=bas)
                {
                    comp tmp=now*f[ba+(len>>1)];
                    f[ba+(len>>1)]=f[ba]-tmp;
                    f[ba]+=tmp;
                }
            }
        }
        if(op==-1)    for(LL i=0;i<lim;++i)    f[i]/=lim;
    }
    poly mulself(poly f)
    {
        LL n=(len(f)<<1)-1; lim=1;
        while(lim<n)    lim<<=1;
        for(LL i=0;i<lim;++i)    rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
        f.resize(lim),fft(f,1);
        for(LL i=0;i<lim;++i)    f[i]*=f[i];
        fft(f,-1),f.resize(n);
        return f;
    }
}using namespace Poly;
LL n,mx,nzr,tmp[400010],a[400010],ans;
const LL lay=50000;
int main()
{
    scanf("%lld",&n);
    poly f(100001);
    for(LL i=1,x;i<=n;++i)
    {
        scanf("%lld",&x);
        mx=max(mx,lay+x);
        f[lay+x]=comp(f[lay+x].real()+1,0);
        nzr+=(x==0),a[i]=x;
    }
    ++mx;
    f.resize(mx);
    f=mulself(f);
    for(LL i=0;i<len(f);++i)    tmp[i]=LL(f[i].real()+0.49);
    for(LL i=1;i<=n;++i)    --tmp[(lay+a[i])<<1];
    for(LL i=1;i<=n;++i)
    {
        ans+=tmp[a[i]+(lay<<1)];
        if(a[i])    ans-=(nzr<<1);
        else    ans-=((nzr-1)<<1);
    }
    printf("%lld\n",ans);
    return 0;
}

Description

Link.

给你一个序列,你每次可以取 $1\sim3$ 个数然后计算和,问你对于每一种和,方案数是多少。

Solution

设一个 OGF $A(x)=\sum_{i=0}^{+\infty}a_{i}x^{i}$,指数为物品的价值,$a_{i}$ 为出现的次数。

也就是说,$\sum a_{i}$ 就是选个一个数的答案。

再来考虑选两个数。$A^{2}(x)$ 显然会有重复。

重复有两种情况,第一种是 $i\times j$ 和 $j\times i$,这个简单,除个 $2!$ 即可。

还有就是 $i\times i$ 的情况,那么再设一个表示一个斧头选两次的 OGF $B(x)=\sum_{i=0}^{+\infty}b_{i}x^{2i}$。

那么选两个数的答案为 $\frac{A^{2}(x)-B(x)}{2!}$。

再来考虑选三个数,基本同理地设 $C(x)=\sum_{i=0}^{+\infty}c_{i}x^{3i}$。

然后选三个数的答案为 $\frac{A^{3}(x)-3A(x)B(x)+2C(x)}{3!}$,这个容斥就是考虑 $\sf aab,baa$ 和 $\sf abc,acb$ 情况的重复。

做完了。

#include<bits/stdc++.h>
using namespace std;
namespace Poly
{
    typedef complex<double> comp;
    typedef vector<complex<double> > poly;
    #define len(x) (int((x).size()))
    const double bh_pi=acos(-1),eps=1e-3;
    int lim,rev[500010];
    void fft(poly &f,int op)
    {
        for(int i=0;i<lim;++i)    if(i<rev[i])    swap(f[i],f[rev[i]]);
        for(int len=2;len<=lim;len<<=1)
        {
            comp bas(cos(2*bh_pi/len),op*sin(2*bh_pi/len));
            for(int fr=0;fr<lim;fr+=len)
            {
                comp now(1,0);
                for(int ba=fr;ba<fr+(len>>1);++ba,now*=bas)
                {
                    comp tmp=now*f[ba+(len>>1)];
                    f[ba+(len>>1)]=f[ba]-tmp;
                    f[ba]+=tmp;
                }
            }
        }
        if(op==-1)    for(int i=0;i<lim;++i)    f[i]/=lim;
    }
    poly mulPoly(poly f,poly g)
    {
        int n=len(f)+len(g)-1;
        for(lim=1;lim<n;lim<<=1);
        for(int i=0;i<lim;++i)    rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
        f.resize(lim),g.resize(lim);
        fft(f,1),fft(g,1);
        for(int i=0;i<lim;++i)    f[i]*=g[i];
        fft(f,-1),f.resize(n);
        return f;
    }
    poly addPoly(poly f,poly g)
    {
        int n=max(len(f),len(g));
        f.resize(n),g.resize(n);
        for(int i=0;i<n;++i)    f[i]+=g[i];
        return f;
    }
    poly decPoly(poly f,poly g)
    {
        int n=max(len(f),len(g));
        f.resize(n),g.resize(n);
        for(int i=0;i<n;++i)    f[i]-=g[i];
        return f;
    }
    poly mulPoly(poly f,double x)
    {
        for(int i=0;i<len(f);++i)    f[i]*=x;
        return f;
    }
    poly divPoly(poly f,double x)
    {
        for(int i=0;i<len(f);++i)    f[i]/=x;
        return f;
    }
}using namespace Poly;
int main()
{
    int waste,x;
    scanf("%d",&waste);
    poly one,two,thr;
    while(waste--)
    {
        scanf("%d",&x);
        if(x>=len(one))    one.resize(x+1);
        if((x<<1)>=len(two))    two.resize(x<<1|1);
        if((x<<1)+x>=len(thr))    thr.resize((x<<1)+x+1);
        one[x]=comp(one[x].real()+1,0);
        two[x<<1]=comp(two[x<<1].real()+1,0);
        thr[(x<<1)+x]=comp(thr[(x<<1)+x].real()+1,0);
    }
    poly ans=addPoly(addPoly(one,divPoly(decPoly(mulPoly(one,one),two),2)),divPoly(addPoly(decPoly(mulPoly(mulPoly(one,one),one),mulPoly(mulPoly(one,two),3)),mulPoly(thr,2)),6));
    for(int i=0;i<len(ans);++i)    if(int(ans[i].real()+eps))    printf("%d %d\n",i,int(ans[i].real()+eps));
    return 0;
}

Descrption

Link.

对于每一个 $i$,求出:

$$ \sum_{j=1}^{i-1}\frac{a_{j}}{(i-j)^{2}}-\sum_{j=i+1}^{n}\frac{a_{j}}{(i-j)^{2}} $$

Solution

令 $f(i)=a_{i},g(i)=\frac{1}{i^{2}}$。

然后

$$ \sum_{j=1}^{i-1}f(j)\times g(i-j)-\sum_{j=i+1}^{n}f(j)\times g(j-i) $$

可以 FFT 了。

#include<bits/stdc++.h>
using namespace std;
const double bh_pi=acos(-1);
int n;
namespace Poly
{
    typedef complex<double> comp;
    typedef vector<complex<double> > poly;
    #define len(x) (int((x).size()))
    int lim,rev[400010];
    void fft(poly &f,int op)
    {
        for(int i=0;i<lim;++i)    if(i<rev[i])    swap(f[i],f[rev[i]]);
        for(int len=2;len<=lim;len<<=1)
        {
            comp bas(cos(2*bh_pi/len),op*sin(2*bh_pi/len));
            for(int fr=0;fr<lim;fr+=len)
            {
                comp now(1,0);
                for(int ba=fr;ba<fr+(len>>1);++ba,now*=bas)
                {
                    comp tmp=now*f[ba+(len>>1)];
                    f[ba+(len>>1)]=f[ba]-tmp;
                    f[ba]+=tmp;
                }
            }
        }
        if(op==-1)    for(int i=0;i<lim;++i)    f[i]/=lim;
    }
    poly mulPoly(poly f,poly g)
    {
        int n=len(f)+len(g)-1;
        for(lim=1;lim<=n;lim<<=1);
        for(int i=0;i<lim;++i)    rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
        f.resize(lim),g.resize(lim);
        fft(f,1),fft(g,1);
        for(int i=0;i<lim;++i)    f[i]*=g[i];
        fft(f,-1),f.resize(n);
        return f;
    }
}using namespace Poly;
int main()
{
    poly f,g;
    scanf("%d",&n);
    f.resize(n+1),g.resize(n+1);
    for(int i=1;i<=n;++i)
    {
        double x;
        scanf("%lf",&x);
        f[i]=comp(x,0);
    }
    for(int i=1;i<=n;++i)    g[i]=comp(1.0/i/i,0);
    poly onetmp=mulPoly(f,g);
    reverse(next(f.begin()),f.end());
    poly anotmp=mulPoly(f,g);
    for(int i=1;i<=n;++i)    printf("%.3lf\n",onetmp[i].real()-anotmp[n-i+1].real());
    return 0;
}

Description

Link.

Summarizing the fucking statement is the last thing in the world I ever want to do.

Solution

我们来重新描述一些概念:

  • 数字牌:筒条万。
  • 顺子:$3$ 张连续的数字牌。(面子)
  • 雀头:$2$ 张完全一样的牌。
  • 刻子:$3$ 张完全一样的牌。(面子)
  • 杠子:$4$ 张完全一样的牌。

观察发现:杠子在除了 $14$ 张牌胡牌情况以外的胡牌情况都出现了,于是又发现:$\binom{4}{3}>\binom{4}{4}$;

于是:

  • 对于胡牌的形式,我们只需要考虑「$3\times4+2$」「七对子」和「国士无双」。

于是我们只需要三种情况取最大即可。

  1. 「七对子」只需要把所有牌型的贡献拉出来,取前 $7$ 个最大的乘起来即可。
  2. 「国士无双」枚举谁来做 $2$ 个的那个,然后取最大。
  3. 「$3\times4+2$」

$\qquad$ 考虑这样一个 DP,设:$f(i,j,k,a,b,c)$ 为前 $i$ 个数,$j$ 个面子,$k$ 个雀头,第 $i$ / $i+1$ / $i+2$ 张牌用了 $a$ / $b$ / $c$ 张的最优答案($k\in\{0,1\}$)。

$\qquad$ $\qquad$ 1. 对于雀头:

$$ f(i,j,1,a+2,b,c)=\max\{\frac{f(i,j,0,a,b,c)\times\text{calc}(i,a+2)}{\text{calc}(i,a)}\} $$

$\qquad$ $\qquad$ $\qquad$ 其中 $\text{calc}(x,y)$ 为第 $x$ 张牌,在手牌中需要出现 $y$ 次,此时对答案的贡献。

$\qquad$ $\qquad$ $\qquad$ 方程的意义即:去掉把第 $i$ 种牌之前的贡献,再算上现在算作雀头的贡献。

$\qquad$ $\qquad$ 2. 对于刻子:

$$ f(i,j+1,0\text{ or }1,a+3,b,c)=\max\{\frac{f(i,j,0\text{ or }1,a,b,c)\times\text{calc}(i,a+3)}{\text{calc}(i,a)}\} $$

$\qquad$ $\qquad$ $\qquad$ 基本同上。

$\qquad$ $\qquad$ 3. 对于顺子:

$$ f(i,j+1,0\text{ or }1,a+1,b+1,c+1)=\max\{\frac{f(i,j,0\text{ or }1,a,b,c)\times\text{calc}(i,a+1)\times\text{calc}(i+1,b+1)\times\text{calc}(i+2,c+1)}{\text{calc}(i,a)\times\text{calc}(i+1,b)\times\text{calc}(i+2,c)}\} $$

$\qquad$ $\qquad$ $\qquad$ 完全同理。

然后就完了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL koshi[15]={0,1,9,10,18,19,27,28,29,30,31,32,33,34}; // the cards that Kokushimusou needs
const bool chunko[36]={0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1}; // the cards which are able to be jyunko
LL getCard()
{
    static char s[10];
    scanf("%s",s+1);
    if(s[1]=='0')    return -1;
    else if(!isdigit(s[1]))
    {
        if(s[1]=='E')    return 28;
        else if(s[1]=='S')    return 29;
        else if(s[1]=='W')    return 30;
        else if(s[1]=='N')    return 31;
        else if(s[1]=='Z')    return 32;
        else if(s[1]=='B')    return 33;
        else    return 34;
    }
    else
    {
        if(s[2]=='m')    return s[1]-'0';
        else if(s[2]=='p')    return s[1]-'0'+9;
        else    return s[1]-'0'+18;
    }
}
LL t,comb[10][10],cnt[40],trs[40],f[40][5][5][5][5][5];
#define calc(x,f) (((cnt[x])<(f)?0:comb[cnt[x]][f])*(trs[x]?(1<<(f)):1))
LL onesolve() // Seven Pairs
{
    vector<LL> pri;
    for(LL i=1;i<=34;++i)    if(cnt[i]>=2)    pri.push_back(calc(i,2));
    sort(pri.begin(),pri.end(),greater<LL>());
    if(pri.size()<size_t(7))    return 0;
    else
    {
        LL res=7;
        for(LL i=0;i<7;++i)    res*=pri[i];
        return res;
    }
}
LL anosolve() // Kokushimusou
{
    LL flag=0;
    for(LL i=1;i<=13;++i)
    {
        if(cnt[koshi[i]]>=2)    flag=1;
        if(cnt[koshi[i]]==0)    return 0;
    }
    if(flag)
    {
        LL res=0;
        for(LL i=1;i<=13;++i)
        {
            LL tmp=13;
            if(cnt[koshi[i]]>=2)
            {
                for(LL j=1;j<=13;++j)    tmp*=calc(koshi[j],(i==j)+1);
            }
            res=max(res,tmp);
        }
        return res;
    }
    else    return 0;
}
void getmax(LL &one,LL ano){one=max(one,ano);}
LL exsolve() // 3x4+2
{
    #define f(i,j,k,a,b,c) (f[i][j][k][a][b][c])
    f(1,0,0,0,0,0)=1;
    for(LL i=1;i<=34;++i)
    {
        for(LL j=0;j<=4;++j)
        {
            for(LL a=0;a<=4;++a)
            {
                for(LL b=0;b<=2;++b)
                {
                    for(LL c=0;c<=2;++c)
                    {
                        if(cnt[i]-a>=2)    getmax(f(i,j,1,a+2,b,c),f(i,j,0,a,b,c)/calc(i,a)*calc(i,a+2));
                        if(j^4)
                        {
                            if(cnt[i]-a>=3)
                            {
                                getmax(f(i,j+1,0,a+3,b,c),f(i,j,0,a,b,c)/calc(i,a)*calc(i,a+3));
                                getmax(f(i,j+1,1,a+3,b,c),f(i,j,1,a,b,c)/calc(i,a)*calc(i,a+3));
                            }
                            if(chunko[i]&&cnt[i]-a>=1&&cnt[i+1]-b>=1&&cnt[i+2]-c>=1&&(b^2)&&(c^2))
                            {
                                getmax(f(i,j+1,0,a+1,b+1,c+1),f(i,j,0,a,b,c)/calc(i,a)/calc(i+1,b)/calc(i+2,c)*calc(i,a+1)*calc(i+1,b+1)*calc(i+2,c+1));
                                getmax(f(i,j+1,1,a+1,b+1,c+1),f(i,j,1,a,b,c)/calc(i,a)/calc(i+1,b)/calc(i+2,c)*calc(i,a+1)*calc(i+1,b+1)*calc(i+2,c+1));
                            }
                        }
                        getmax(f(i+1,j,0,b,c,0),f(i,j,0,a,b,c));
                        getmax(f(i+1,j,1,b,c,0),f(i,j,1,a,b,c));
                    }
                }
            }
        }
    }
    LL res=0;
    for(LL i=1;i<=34;++i)
    {
        for(LL a=0;a<=4;++a)
        {
            for(LL b=0;b<=2;++b)
            {
                for(LL c=0;c<=2;++c)    getmax(res,f(i,4,1,a,b,c));
            }
        }
    }
    #undef f
    return res;
}
int main()
{
    for(LL i=0;i<=4;++i)    comb[i][0]=comb[i][i]=1;
    for(LL i=1;i<=4;++i)    for(LL j=1;j<i;++j)    comb[i][j]=comb[i-1][j-1]+comb[i-1][j];
    scanf("%lld",&t);
    while(t--)
    {
        memset(f,0,sizeof(f));
        for(LL i=1;i<=34;++i)    cnt[i]=4,trs[i]=0;
        LL tmp=getCard();
        while(~tmp)    --cnt[tmp],tmp=getCard();
        tmp=getCard();
        while(~tmp)    trs[tmp]=1,tmp=getCard();
        printf("%lld\n",max(max(onesolve(),anosolve()),exsolve()));
    }
    return 0;
}

Description

Link.

给定一棵 $n$ 个点的树,设 $E$ 为边集,$V'_x,\ V'_y$ 分别为删去边 $(x,y)$ 后 点 $x$ 所在的树的点集和点 $y$ 所在的树的点集,求:

$$ \sum_{(u,v)\in E}(\sum_{x\in V'_{u}}[x\text{ is the centroid of }V'_{u}]\times x+\sum_{y\in V'_{v}}[y\text{ is the centroid of }V'_{v}]\times y) $$

Solution

重心,想到重儿子,我们记一个结点 $u$ 的重儿子为 $hb_{u}$。

对于 $\text{subtree}(u)$,如果 $u$ 不是 $\text{subtree}(u)$ 的 centroid,那么 $\text{subtree}(u)$ 的 centroid 一定在 $\text{subtree}(hb(u))$ 里。

然后我们找到对于 $u$ 最深的一个重儿子 $v$(就是重链上的某个结点),满足 $siz_{u}-siz_{v}\le\frac{siz_{u}}{2}$,那么 $v$ 就是重心(还有 $fa_{v}$ 需要判断一下)。

对于这道题,我们直接枚举每条边 $(u,v)$,设 $u$ 比 $v$ 浅,那么 $v$ 就是 $V'_{v}$ 的根,直接套就可以了。

对于 $u$,我们换个根也就出来了,具体来说是交换 $(u,v)$ 的父子关系,不然直接交换 $(1,x)$ 太劣。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<int> e[300010];
int t,n,siz[300010],hb[300010][20],fa[300010];
LL ans;
void dfs(int x,int las)
{
    siz[x]=1,fa[x]=las;
    for(unsigned int i=0;i<e[x].size();++i)
    {
        int y=e[x][i];
        if(y^las)
        {
            dfs(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[hb[x][0]])    hb[x][0]=y;
        }
    }
    for(int i=1;i^20;++i)    hb[x][i]=hb[hb[x][i-1]][i-1];
}
void cgfather(int x,int y)
{
    siz[x]-=siz[y],siz[y]+=siz[x];
    fa[x]=y,fa[y]=0;
    if(hb[x][0]==y)
    {
        hb[x][0]=0;
        for(unsigned int i=0;i<e[x].size();++i)    if((e[x][i]^y)&&siz[e[x][i]]>siz[hb[x][0]])    hb[x][0]=e[x][i];
        for(int i=1;i^20;++i)    hb[x][i]=hb[hb[x][i-1]][i-1];
    }
    if(siz[x]>siz[hb[y][0]])
    {
        hb[y][0]=x;
        for(int i=1;i^20;++i)    hb[y][i]=hb[hb[y][i-1]][i-1];
    }
}
void getans(int x)
{
    #define eplist(x,all) (max(siz[hb[x][0]],(all)-siz[x])<=((all)>>1))
    int now=x;
    for(int i=19;~i;--i)    if(hb[now][i]&&siz[x]-siz[hb[now][i]]<=(siz[x]>>1))    now=hb[now][i];
    if(eplist(now,siz[x]))    ans+=now;
    if(eplist(fa[now],siz[x]))    ans+=fa[now];
    #undef eplist
}
void exdfs(int x,int las)
{
    for(unsigned int i=0;i<e[x].size();++i)
    {
        int y=e[x][i];
        if(y^las)
        {
            getans(y);
            cgfather(x,y);
            getans(x);
            exdfs(y,x);
            cgfather(y,x);
        }
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1,x,y;i<n;++i)
        {
            scanf("%d %d",&x,&y);
            e[x].push_back(y);
            e[y].push_back(x);
        }
        dfs(1,0),exdfs(1,0);
        printf("%lld\n",ans);
        for(int i=1;i<=n;++i)    e[i].clear(),siz[i]=hb[i][0]=fa[i]=0;
        ans=0;
    }
    return 0;
}