zoukankan      html  css  js  c++  java
  • 题解(新)

    lg4705:原式经过转化可以变成卷积的形式,但是这样子也很难做。

    题目要我们求$frac{a[i]^v}{i!}$,需要把它变为生成函数

    后面的东西a[i]^v,它的生成函数是$frac{1}{1-a[i]x}$

    这道题可以构造2个ln函数,然后答案就成为了n-x*(ln(1-a[i]x))'

    使用分治fft计算1-a[i]*x后ln,求导,用n减去,每个元素乘以i!,最后卷积即可。

    lg4389:

    这道题是一个背包问题。

    考虑写出每种物品的普通生成函数:是$1+x^i+x^{2i}+x^{3i}+....$

    原因是这道题物品可以选择无数次。

    这个函数就是$frac{1}{1-x^i}$

    可以使用分治fft把所有的多项式乘起来,但是无法通过此题。

    实际上这道题可以先ln,再指数相加,最后exp,但是也过不了这道题。需要挖掘这个函数的性质。

    分治fft的做法扩展难度较大,所以考虑扩展ln做法。

    把函数求导一下,则根据复合函数求导法则,假设右边是g(x),f(x)=1-x^i则$g(x)'=frac{f'(x)}{f(x)}$

    把原式使用生成函数展开。则$g(x)'=-sum vx^{v-1+vi}$

    还原函数(不用导数了),变成$g(x)=-sum frac{vx^{v+vi}}{v+vi}$

    这个式子还是不好看,可以把i从0枚举变成从1枚举,上面下面都除以v,变成$g(x)=-sum frac{x^{vi}}{i}$

    可以统计每种物品的个数,再加上对应的系数,exp回来,最终求逆即可。

    这道题的思路十分经典,值得学习。

    lg5162

    先dp,再构造指数型生成函数计算即可。要用多项式求逆。

    lg5219

    这道题要使用prufer序列做。

    uoj79

    一般图最大匹配。

    一般图最大匹配与二分图最大匹配相似,但是多了处理奇环的过程。

    最大匹配的思想是:每次寻找一个未被匹配的点,走到另一个未被匹配的点,翻转路径上的所有边。

    可以证明这样子翻转若干次直到没有增广路时,是最优的。

    在二分图这样子一定不会经过重复的点。(画一下图可以知道)

    但是一般图中有奇环,所以可能经过重复的点,算法会死循环。

    所以要处理奇环。

    BFS整张图,把队列中存在过的点染黑,黑点的匹配点染白。

    每次从队列中取出一个点。枚举它的出点,设为v

    若v在当前bfs中未被访问过:则找到了一个匹配,直接返回。

    否则把它的匹配点插入队列。

    若v被访问过,则发现找到了一个环。

    如果这个环是偶环,则不用考虑,一定不会走重复点。

    但是如果这个环是奇环,则要处理。

    设环边为(u,v),lca为h

    把这个环缩起来并且再次寻找增广路。

    证明:如果原图为g,缩点后的图为g'

    则要证明g中的增广路,g'也有

    g'有的增广路,g中也有。

    当g有增广路,发现h->s上的第一条边一定是匹配边。

    如果不是,可以走到一个环上的未盖点继续增广。

    设s->h这个路径为p。

    令gs,g's为g,g'把这条路径取反后的结果。显然匹配大小不变。

    若g存在增广路,则g的最大匹配值应该更大,gs的最大匹配值也应该更大,所以gs也有增广路。

    设gs的增广路为c,则如果c没有经过旧环,则g's也包含c

    否则,让g's的这一条增广路c走到h,则发现肯定有一种走法可以走交错边。

    因为g's的匹配数和g'相同,所以根据增广路定理,g'也有增广路。

    当g'有增广路,如果没走新点,则g中也有。

    如果走了新点,则走新点肯定是走过匹配边,非匹配边。

    匹配边肯定是和h相连的。

    由于环是奇环,且匹配边交错,所以一定可以找到一种走法走到h。

    uoj171

    这道题是一个一般图最大匹配建模,十分巧妙。

    建模的方法是:对于每个篮子拆成3个点,这3个点之间两两连边。

    对于每个点向它能被放置的篮子连边。

    然后求一般图最大匹配,答案再减去n。

    原因是:

    当一个篮子没有连匹配边(没有球被放在这个篮子里),则贡献为1

    当一个篮子连了1条匹配边(有1个球被放在这个篮子里),则贡献为2

    当一个篮子连了2条匹配边(有2个球被放在这个篮子里),则贡献为2

    当一个篮子连了3条匹配边(有3个球被放在这个篮子里),则贡献为3

    减去连的匹配边的个数。(它们的和为球的个数),

    当一个篮子没有连匹配边(没有球被放在这个篮子里),则贡献为1

    当一个篮子连了1条匹配边(有1个球被放在这个篮子里),则贡献为1

    当一个篮子连了2条匹配边(有2个球被放在这个篮子里),则贡献为0

    当一个篮子连了3条匹配边(有3个球被放在这个篮子里),则贡献为0

    恰好为题目所要求的贡献。

    注意,要从大->小增广,这样子在增广的过程中不会让大的点(即球点)失配(观察翻转边的过程可得)。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1010
    int h[N],v[N*N*4],nxt[N*N*4],ec,n,m,x,y,ct,pr[N],mt[N],id[N],p[N],vi[N],t,e;
    void add(int x,int y){v[++ec]=y;nxt[ec]=h[x];h[x]=ec;}
    int fd(int x){return p[x]==x?x:p[x]=fd(p[x]);}
    void adj(int x,int y){add(x,y);add(y,x);}
    queue<int>q;
    int lca(int x,int y){
        ct++;
        x=fd(x);
        y=fd(y);
        while(id[x]!=ct){
            id[x]=ct;
            x=fd(pr[mt[x]]);
            if(y)swap(x,y);
        }
        return x;
    }
    inline char nc(){
        static char buf[1000000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int rd(){
        char ch=nc();int sum=0;
        while(!(ch>='0'&&ch<='9'))ch=nc();
        while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
        return sum;
    }
    void bb(int x,int y,int z){
        while(fd(x)!=z){
            pr[x]=y;
            y=mt[x];
            if(vi[y]==2){
                vi[y]=1;
                q.push(y);
            }
            if(fd(x)==x)p[x]=z;
            if(fd(y)==y)p[y]=z;
            x=pr[y];
        }
    }
    int ag(int s){
        memset(pr,0,sizeof(pr));
        memset(vi,0,sizeof(vi));
        vi[s]=1;
        while(!q.empty())q.pop();
        q.push(s);
        for(int i=1;i<=n+m*3;i++)p[i]=i;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=h[x];i;i=nxt[i]){
                if(fd(x)==fd(v[i])||vi[v[i]]==2)continue;
                if(!vi[v[i]]){
                    vi[v[i]]=2;
                    pr[v[i]]=x;
                    if(!mt[v[i]]){
                        int x=v[i],la;
                        for(;x;x=la){
                            la=mt[pr[x]];
                            mt[x]=pr[x];
                            mt[pr[x]]=x;
                        }
                        return 1;
                    }
                    vi[mt[v[i]]]=1;
                    q.push(mt[v[i]]);
                }
                else{
                    int d=lca(x,v[i]);
                    bb(x,v[i],d);
                    bb(v[i],x,d);
                }
            }
        }
        return 0;
    }
    int main(){
        t=rd();
        while(t--){
            memset(h,0,sizeof(h));
            memset(mt,0,sizeof(mt));
            memset(id,0,sizeof(id));
            ec=0;
            n=rd();
            m=rd();
            e=rd();
            for(int i=1;i<=m;i++){
                adj(i*3-2,i*3-1);
                adj(i*3-1,i*3);
                adj(i*3,i*3-2);
            }
            while(e--){
                x=rd();
                y=rd();
                adj(x+3*m,y*3-2);
                adj(x+3*m,y*3-1);
                adj(x+3*m,y*3);
            }
            int r=0;
            for(int i=n+m*3;i;i--)
                if(!mt[i])r+=ag(i);
            cout<<r-n<<'
    ';
            for(int i=m*3+1;i<=n+m*3;i++)
                cout<<ceil((double)mt[i]/3.0)<<' ';
            cout<<'
    ';
        }
    }
    View Code

    bzoj1135

    hall定理的运用。

    这道题中,第i个人可以穿i~i+d的鞋子,所以实际上这道题就是个二分图匹配模型。

    右边有n个点,表示鞋子,左边初始没有点,每次增加/减少一些点。

    操作有2种:增加k个点,类型为i,这k个点向i->i+d连边,然后要求二分图是否有完全匹配。

    减少k个点,类型为i,这k个点向i->i+d连边,然后要求二分图是否有完全匹配。

    这样子显然是会超时的,要考虑hall定理。

    发现每一种人可以当做整体考虑。因为这些人对应的右边的点数都是一样的,都是d,若取这些人的子集进行判断,则对于右边的点数还是d,但是左边的点数减少,更符合要求。

    发现只需要对于每个区间考虑是否合法即可。因为如果选出一些连通块,则实际上要求就是这些连通块是否分别满足hall定理。

    因为把这些连通块和它对应的点(设为t)的大小加起来,大小>=这些连通块全体对应

    如果这些连通块(大小设为s)全都分别满足要求,且加起来不满足要求,由于右边的连通块是有重复的(设值为v)

    则要满足s<=v<=t,s>=v,矛盾。所以只要分别判定每个连通块(就是所有区间)是否合法即可。

    如果中间区间有交的话,则把中间的补进来,二分图左边的点数会增大,右边不变,限制更强。

    所以只需要判定每个区间是否合法即可。

    可以得到s<=(r-l+1+d)*k

    把所有数减去k,则得到s<=d*k

    这个用线段树维护最大子段和即可。

    lg4002

    prufer序列:prufer序列和一棵无向完全图的生成树一一对应。

    构造prufer序列的方法是:从prufer序列中选出一个叶子节点,且编号最小,删除这个叶子节点,把它添加到答案序列的末尾。

    一直这样,直到树上只剩下2个节点,答案序列的大小就为n-2,得到了prufer序列。

    这个序列有2个性质:每个元素的取值可以为任意(这说明无根树的大小有n^(n-2)个),每个节点的度数为它在这个序列的出现次数+1。

    这道题主要要使用第二个性质。

    这个公式描述了初始的答案。

    d[i]表示第i个元素的出现次数。前面的表示这i个元素可以被分布在这个序列的任何位置。后面的a[i]^(d[i]+1)表示每个连通块大小伟a[i],可以连出d[i]+1条边,后面的式子为权值的定义。

    如果把一个a[i]提取出来的话,则公式变为:

    这个公式转换了枚举顺序。

    前面的式子(n-2)!*a[i]可以O(n)计算,后面的式子如果硬算时间复杂度很高。所以要使用生成函数优化。

    构造2个生成函数a,b,且定义f

    f实际上可以被变成除法的形式:

    后面的可以使用套路,先ln,再转成加法,最后exp回来,变成:

    但是每个式子还要乘以一个系数,类似lg4705那样做即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define mo 998244353
    #define N 5000010
    #define ll unsigned long long
    #define int long long
    #define pl vector<int>
    int qp(int x,int y){
        int r=1;
        for(;y;y>>=1,x=1ll*x*x%mo)
            if(y&1)r=1ll*r*x%mo;
        return r;
    }
    int rev[N],v,le,w[N],p[N];
    void deb(pl x){
        for(int i:x)cout<<i<<' ';
        puts("");
    }
    void init(int n){
        v=1;
        le=0;
        while(v<n)le++,v*=2;
        for(int i=0;i<v;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(le-1));
        int g=qp(3,(mo-1)/v);
        w[v/2]=1;
        for(int i=v/2+1;i<v;i++)
            w[i]=1ull*w[i-1]*g%mo;
        for(int i=v/2-1;~i;i--)
            w[i]=w[i*2];
    }
    void fft(int v,pl &a,int t){
        static unsigned long long b[N];
        int s=le-__builtin_ctz(v);
           for(int i=0;i<v;i++)
               b[rev[i]>>s]=a[i];
        int c=0;
        w[0]=1;
        for(int i=1;i<v;i*=2,c++)
            for(int r=i*2,j=0;j<v;j+=r)
                for(int k=0;k<i;k++){
                       int tx=b[j+i+k]*w[k+i]%mo;
                    b[j+i+k]=b[j+k]+mo-tx;
                    b[j+k]+=tx;
                }
        for(int i=0;i<v;i++)
            a[i]=b[i]%mo;
        if(t==0)return;
        int iv=qp(v,mo-2);
        for(int i=0;i<v;i++)
            a[i]=1ull*a[i]*iv%mo;
        a.resize(v);
        reverse(a.begin()+1,a.end());
    }
    pl operator *(pl x,pl y){
        int s=x.size()+y.size()-1;
        if(x.size()<=20||y.size()<=20){
            pl r;
            r.resize(s);
            for(int i=0;i<x.size();i++)
                for(int j=0;j<y.size();j++)
                    r[i+j]=(r[i+j]+x[i]*y[j])%mo;
            return r;
        }
        init(s);
        x.resize(v);
        y.resize(v);
        fft(v,x,0);
        fft(v,y,0);
        //deb(x);
        //deb(y);
        for(int i=0;i<v;i++)
            x[i]=x[i]*y[i]%mo;
        fft(v,x,1);
        x.resize(s);
        return x;
    }
    void inv(int n,pl &b,pl &a){
        if(n==1){
            b[0]=qp(a[0],mo-2);
            return;
        }
        inv((n+1)/2,b,a);
        static pl c;
        init(n*2);
        c.resize(v);
        b.resize(v);
        for(int i=0;i<n;i++)
            c[i]=a[i];
        fft(v,c,0);
        //deb(c);
        fft(v,b,0);
        //deb(b);
        for(int i=0;i<v;i++)
            b[i]=1ll*(2ll-1ll*c[i]*b[i]%mo+mo)%mo*b[i]%mo;
        //deb(b);
        fft(v,b,1);  
           b.resize(n);
           //deb(b);
    }
    void ad(pl &x,pl y,int l){
        x.resize(max((int)x.size(),(int)y.size()+l));
        for(int i=0;i<y.size();i++)
            x[i+l]=(x[i+l]+y[i])%mo;
    }
    pl operator +(pl x,pl y){
        ad(x,y,0);
        return x;
    }
    pl iv(pl x){
        pl y;
        int n=x.size();
        y.resize(n);
        inv(n,y,x);
        y.resize(n);
        return y;
    }
    pl operator -(pl x,pl y){
        int s=max(x.size(),y.size());
        x.resize(s);
        y.resize(s);
        for(int i=0;i<s;i++)
            x[i]=(x[i]-y[i]+mo)%mo;
        return x;
    }
    pl qd(pl x){
        pl y;
        int n=x.size();
        y.resize(n-1);
        //deb(x);
        for(int i=0;i<n-1;i++)
            y[i]=x[i+1]*(i+1)%mo;
        //deb(y);
        return y;
    }
    pl jf(pl x){
        int n=x.size();
        pl y;
        y.resize(n+1);
        for(int i=1;i<=n;i++)
            y[i]=x[i-1]*qp(i,mo-2)%mo;
        return y;
    }
    pl ln(pl x){
        int n=x.size();
        pl y=qd(x),z=iv(x);
        y=y*z;
        y=jf(y);
        y.resize(n);
        return y;
    }
    inline char nc(){
        static char buf[500000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,500000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int rd(){
        char ch=nc();int sum=0;
        while(!(ch>='0'&&ch<='9'))ch=nc();
        while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
        return sum;
    }
    char bf[100];
    void wr(int x){
        if(!x){
            putchar('0');
            putchar(' ');
            return;
        }
        int ct=0;
        while(x){
            bf[++ct]=x%10;
            x/=10;
        }
        for(int i=ct;i;i--)
            putchar(bf[i]+'0');
        putchar('
    ');
    }
    void gt(int n,pl &y,pl x){
        if(n==1){
            y.resize(1);
            y[0]=1;
            return;
        }
        gt((n+1)/2,y,x);
        pl z=x,a;
        z.resize(n);
        y.resize(n);
        a.resize(1);
        a[0]=1;
        y=y*(a-ln(y)+z);
        y.resize(n);
    }
    pl ep(pl x){
        pl y;
        int n=x.size();
        gt(n,y,x);
        return y;
    }
    void put(pl a){
        for(int i=0;i<a.size();i++)
            printf("%lld ",a[i]);
        puts("");
    }
    int n,a[N],lm,jc[N],ij[N],m,b[N],k;
    pl ff(int *a,int l,int r){
        if(l==r){
            pl c;
            c.resize(2);
            c[0]=1;
            c[1]=(-a[l]+mo)%mo;
            return c;
        }
        return ff(a,l,(l+r)/2)*ff(a,(l+r)/2+1,r);
    }
    pl gt(pl x,int n){
        pl y,z;
        y.resize(2);
        y[1]=1;
        z.resize(1);
        z[0]=n;
        x=x*y;
        x[0];
        x[1];
        return z-x;
    }
    signed main(){
        cin>>n>>m;
        if(n==1){
            if(m==0)puts("1");
            else puts("0"); 
            return 0;
        } 
        jc[0]=ij[0]=1;
        for(int i=1;i<N;i++)
            jc[i]=1ll*jc[i-1]*i%mo;
        ij[N-1]=qp(jc[N-1],mo-2);
        for(int i=N-1;i;i--)
            ij[i-1]=1ll*ij[i]*i%mo;
        int r=1;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            r=r*a[i]%mo;
        }
        pl b=ff(a,1,n);
        b=ln(b);
        b=qd(b);
        b.push_back((mo-b[n-1])%mo);
        for(int i=n-1;i;i--)
            b[i]=(mo-b[i-1])%mo;
        b[0]=n;
        pl e,f;
        e.resize(n+1);
        f.resize(n+1);
        for(int i=0;i<=n;i++){
            e[i]=qp(i+1,2*m)*ij[i]%mo;
            f[i]=qp(i+1,m)*ij[i]%mo;
        }
        e=e*iv(f);
        e.resize(n+1);
        f=ln(f);
        for(int i=0;i<=n;i++){
            e[i]=e[i]*b[i]%mo;
            f[i]=f[i]*b[i]%mo;
        }
        f=ep(f);
        e=e*f;
        cout<<e[n-2]*jc[n-2]%mo*r%mo;
    }
    View Code

    uoj77

    这道题可以想到最小割建图。

    一个点被分在左边表示选黑色,右边表示选白色。

    如果只有区间连边限制的话,可以这样建图:

    s->i连w,i->t连b,对于每个i,新建i'->i连p

    如果j按题目要求(在一个区间内)可以让i成为奇怪的格子的话,让j向i'连inf边。

    这样子如果j选了黑色,那么j,i'要在同一边,要么获得b的代价(选择黑色),要么获得w+p的代价(获得白色和奇怪的代价)

    使用线段树优化建边。建立一棵线段树,线段树每个儿子节点向父亲连边。

    对于每个i,在线段树上定位出[l[i],r[i]],把这些定位点向i'连边(流量inf),i'向i连边,s向i连边,i向t连边。

    对于每个i,在线段树上给叶子连边。流量均为inf

    实际上,儿子->父亲连的好处是:儿子的所有流量都可以流向父亲且不被限制,父亲可以连向另外的点,达到了区间连边的限制。

    现在有前缀连边的限制,要维护前缀的线段树(可持久化线段树)

    把每个新建的点往后面对应的点连边,流量inf。

    插入以后查询,向对应的点连边,流量inf。

    这样就能解决这道题了。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1500010
    int h[N],nxt[N],v[N],w[N],s,t,dep[N],ec,n,ct,cc,lc[N],rc[N],rt[N];
    void add(int a,int b,int c){v[++ec]=b;w[ec]=c;nxt[ec]=h[a];h[a]=ec;}
    void adj(int x,int y,int z){add(x,y,z);add(y,x,0);}
    bool bfs(){
        queue<int>q;q.push(s);memset(dep,0,sizeof(dep));dep[s]=1;
        while(!q.empty()){
            int x=q.front();q.pop();
            for(int i=h[x];i;i=nxt[i])
                if(w[i]>0&&!dep[v[i]])
                    dep[v[i]]=dep[x]+1,q.push(v[i]);
        }return dep[t];
    }
    int dfs(int x,int dis){
        if(x==t||!dis)return dis;int tp=0;
        for(int i=h[x];i&&tp<dis;i=nxt[i])
            if(dep[v[i]]==dep[x]+1&&w[i]>0){
                int f=dfs(v[i],min(dis-tp,w[i]));
                w[i]-=f;w[i^1]+=f;tp+=f;
            }
        if(!tp)dep[x]=0;return tp;
    }
    int din(){
        int aans=0;
        while(bfs())aans+=dfs(s,1e9);
        return aans;
    }
    void q(int o,int l,int r,int x,int y){
        if(!o||r<x||y<l)return;
        if(x<=l&&r<=y){
            adj(o,cc,1e9);
            return;
        }
        int md=(l+r)/2;
        q(lc[o],l,md,x,y);
        q(rc[o],md+1,r,x,y);
    }
    void ins(int &o,int p,int l,int r,int x,int y){
        o=++ct;
        lc[o]=lc[p];
        rc[o]=rc[p];
        if(p)adj(p,o,1e9);
        adj(y,o,1e9);
        if(l==r)return;
        int md=(l+r)/2;
        x<=md?ins(lc[o],lc[p],l,md,x,y):ins(rc[o],rc[p],md+1,r,x,y);
    }
    int main(){
        int rr=0;
        ec=1;
        cin>>n;
        s=n*35;
        t=s+1;
        ct=n*2;
        for(int i=1;i<=n;i++){
            int u,b,w,l,r,p;
            cin>>u>>b>>w>>l>>r>>p;
            rr+=b;
            rr+=w;
            cc=i+n;
            adj(s,i,w);
            adj(i,t,b);
            adj(i+n,i,p);
            q(rt[i-1],0,1000000000,l,r);
            ins(rt[i],rt[i-1],0,1000000000,u,i);
        }
        cout<<rr-din();
    }
    View Code

    lg4292

    法1:点分。先分数规划,把每个数都减去mid,则问题转化为询问是否有一条路径长度在[l,r]内且权值>=0

    寻找过分治重心的路径。把路径按照长度降序排序,枚举一条路径,如果这条路径的边数为j,则另外一条路径的边数要为[l-j,r-j]。这个可以使用单调队列维护。

    法2:长剖+线段树。先长剖,再求出长剖序。

    长剖序就是每次先dfs长儿子,再dfs短儿子。这样子做的好处是:每一条链都是线段树上的一段连续的节点。

    设一棵线段树,上面每一个节点存储的都是:dfs序为x的节点到根的信息。

    设f[x][i]表示节点x到儿子的距离为i,根->i的最大权值。

    这个可以长链剖分O(n)求出。

    在统计答案时,枚举每一条链和对应的长度,假设这个长度为j,则另外一条路径的长度要为[l-j,r-j],可以线段树维护。

    在统计完答案时,把现在这个子树(短儿子)合并到线段树中。

    注意到上面的f可以在线段树合并贡献时顺便求出来,省掉求f的代码。

    代码很好写。

    #include<bits/stdc++.h>
    #define db double
    #define N 400010
    #define int long long
    using namespace std;
    int h[N],v[N],nxt[N],ec,id[N],dd[N],ct,p[N],n,l,u,x,y,z,d[N],po[N];
    db mx[N],w[N],aa,dp[N],va[N];
    void add(int x,int y,int z){v[++ec]=y;w[ec]=z;nxt[ec]=h[x];h[x]=ec;}
    void bd(int o,int l,int r){
        mx[o]=-1e18;
        if(l==r)return;
        int md=(l+r)/2;
        bd(o*2,l,md);
        bd(o*2+1,md+1,r);
    }
    db q(int o,int l,int r,int x,int y){
        if(r<x||y<l)return -1e18;
        if(x<=l&&r<=y)return mx[o];
        int md=(l+r)/2;
        return max(q(o*2,l,md,x,y),q(o*2+1,md+1,r,x,y));
    }
    void mod(int o,int l,int r,int x,db y){
        mx[o]=max(mx[o],y);
        if(l==r)return;
        int md=(l+r)/2;
        x<=md?mod(o*2,l,md,x,y):mod(o*2+1,md+1,r,x,y);
    }
    void d1(int x,int fa){
        for(int i=h[x];i;i=nxt[i])
            if(v[i]!=fa){
                d1(v[i],x);
                if(dd[v[i]]>dd[p[x]])p[x]=v[i];
            }
        dd[x]=dd[p[x]]+1;
    }
    void d2(int x,int fa){
        po[x]=++ct;
        if(p[x])d2(p[x],x);
        for(int i=h[x];i;i=nxt[i])
            if(v[i]!=fa&&v[i]!=p[x])
                d2(v[i],x);
    }
    void d3(int x,int fa){
        mod(1,1,n,po[x],dp[x]);
        for(int i=h[x];i;i=nxt[i])
            if(v[i]==p[x]){
                dp[v[i]]=dp[x]+w[i];
                d3(v[i],x);
            }
        for(int i=h[x];i;i=nxt[i])
            if(v[i]!=fa&&v[i]!=p[x]){
                dp[v[i]]=dp[x]+w[i];
                d3(v[i],x);
                for(int j=1;j<=dd[v[i]];j++){
                    va[j]=q(1,1,n,po[v[i]]+j-1,po[v[i]]+j-1);
                    aa=max(aa,q(1,1,n,max(po[x],po[x]+l-j),min(n,min(po[x]+u-j,po[x]+dd[x]-1)))+va[j]-2*dp[x]);
                }
                for(int j=1;j<=dd[v[i]];j++)
                    mod(1,1,n,po[x]+j,va[j]);
            }
        aa=max(aa,q(1,1,n,po[x]+l,min(n,min(po[x]+u,po[x]+dd[x]-1)))-dp[x]);
    }
    signed main(){
        cin>>n>>l>>u;
        for(int i=1;i<n;i++){
            scanf("%lld%lld%lld",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
        }
        d1(1,0);
        d2(1,0);
        db l=0,r=1e6;
        while(l+1e-5<r){
            db md=(l+r)/2;
            for(int i=1;i<=ec;i++)
                w[i]-=md;
            bd(1,1,n);
            aa=-1e18;
            d3(1,0);
            if(aa<0)r=md;
            else l=md;
            for(int i=1;i<=ec;i++)
                w[i]+=md;
        }
        printf("%.3lf
    ",l);
    }
    View Code

    lg6515

    以后要多做计算几何和线性代数的题目。

    发现一个(a,b)当一维为负时,无论怎么操作都不能使两维同时变正。

    这道题可以把一对(a,b)视为向量,则变换都是在向量上乘以一个矩阵。

    实际上,现在的(a,b)可以被表示成(ax+by,cx+dy)(x,y为原向量)。

    如果向量在"合法平面"上,则这个向量还是可以被操作的。

    所以过程未结束的条件可以被写成向量在"合法平面"上。

    每次1操作相当于把平面在下面的部分砍掉一些。操作2相当于打个标记,在下面一次操作(一定是1操作)把平面砍掉一些。

    把所有向量按极角从小到大排序,则结束条件可以被写成:"合法平面"在排序后数组位置相邻2个点之间。

    枚举每一对相邻的向量。分3种情况讨论:

    1.当前向量两个都在y=x下面。用1操作会砍掉y=x下面的平面,这两个向量也会被砍掉。所以只能用2操作。

    2.当前向量两个都在y=x上面,只能用1操作。若进行2操作,则反转后会变成1情况,只能继续用2操作。

    3.当前向量一个在y=x下面,一个在上面。只要进行操作1,下面的向量就不可操作。

    这时不应该继续进行操作1。如果继续进行操作1,可以对称后再进行操作1。如果未对称后继续砍平面,则不能让合法平面上砍掉上面的向量。这样子不符合要求。

    所以应该对称后再砍。

    实际上,一开始也可能对称,这样子可能步数更小。所以需要比较两种方法的优劣,再决定怎么操作。

    时间复杂度:情况1会转变成情况2,但是情况2会持续很多次。这是瓶颈。

    所以可以使用除法算出2要进行的次数。

    时间复杂度分析同辗转相除。

    lg3789

    #include<bits/stdc++.h>
    using namespace std;
    #define mo 998244353
    #define N 5000010
    #define ll unsigned long long
    #define int long long
    #define pl vector<int>
    int qp(int x,int y){
        int r=1;
        for(;y;y>>=1,x=1ll*x*x%mo)
            if(y&1)r=1ll*r*x%mo;
        return r;
    }
    int m,rev[N],v,le,w[N],p[N];
    int n,a[N],lm,jc[N],ij[N],ml,k,g[N];
    void deb(pl x){
        for(int i:x)cout<<i<<' ';
        puts("");
    }
    void init(int n){
        v=1;
        le=0;
        while(v<n)le++,v*=2;
        for(int i=0;i<v;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(le-1));
        int g=qp(3,(mo-1)/v);
        w[v/2]=1;
        for(int i=v/2+1;i<v;i++)
            w[i]=1ull*w[i-1]*g%mo;
        for(int i=v/2-1;~i;i--)
            w[i]=w[i*2];
    }
    void fft(int v,pl &a,int t){
        static unsigned long long b[N];
        int s=le-__builtin_ctz(v);
           for(int i=0;i<v;i++)
               b[rev[i]>>s]=a[i];
        int c=0;
        w[0]=1;
        for(int i=1;i<v;i*=2,c++)
            for(int r=i*2,j=0;j<v;j+=r)
                for(int k=0;k<i;k++){
                       int tx=b[j+i+k]*w[k+i]%mo;
                    b[j+i+k]=b[j+k]+mo-tx;
                    b[j+k]+=tx;
                }
        for(int i=0;i<v;i++)
            a[i]=b[i]%mo;
        if(t==0)return;
        int iv=qp(v,mo-2);
        for(int i=0;i<v;i++)
            a[i]=1ull*a[i]*iv%mo;
        a.resize(v);
        reverse(a.begin()+1,a.end());
    }
    pl operator *(pl x,pl y){
        int s=x.size()+y.size()-1;
        if(x.size()<=20||y.size()<=20){
            pl r;
            r.resize(s);
            for(int i=0;i<x.size();i++)
                for(int j=0;j<y.size();j++)
                    r[i+j]=(r[i+j]+x[i]*y[j])%mo;
            r.resize(k+1);
            return r;
        }
        init(s);
        x.resize(v);
        y.resize(v);
        fft(v,x,0);
        fft(v,y,0);
        //deb(x);
        //deb(y);
        for(int i=0;i<v;i++)
            x[i]=x[i]*y[i]%mo;
        fft(v,x,1);
        x.resize(s);
        return x;
    }
    void inv(int n,pl &b,pl &a){
        if(n==1){
            b[0]=qp(a[0],mo-2);
            return;
        }
        inv((n+1)/2,b,a);
        static pl c;
        init(n*2);
        c.resize(v);
        b.resize(v);
        for(int i=0;i<n;i++)
            c[i]=a[i];
        fft(v,c,0);
        //deb(c);
        fft(v,b,0);
        //deb(b);
        for(int i=0;i<v;i++)
            b[i]=1ll*(2ll-1ll*c[i]*b[i]%mo+mo)%mo*b[i]%mo;
        //deb(b);
        fft(v,b,1);  
           b.resize(n);
           //deb(b);
    }
    void ad(pl &x,pl y,int l){
        x.resize(max((int)x.size(),(int)y.size()+l));
        for(int i=0;i<y.size();i++)
            x[i+l]=(x[i+l]+y[i])%mo;
    }
    pl operator +(pl x,pl y){
        ad(x,y,0);
        return x;
    }
    pl iv(pl x){
        pl y;
        int n=x.size();
        y.resize(n);
        inv(n,y,x);
        y.resize(n);
        return y;
    }
    pl operator /(pl a,pl y){
        int n=a.size()-1,m=y.size()-1;
        pl x,b,t;
        x.resize(n+1);
        b.resize(m+1);
        for(int i=0;i<=n;i++)
            x[n-i]=a[i];
        for(int i=0;i<=m;i++)
            b[m-i]=y[i];
        for(int i=n-m+2;i<=m;i++)
            b[i]=0;
        b.resize(n-m+1);
        t=iv(b);
        //deb(t);
        //deb(x);
        //deb(t);
        x=x*t;
        //deb(x);
        x.resize(n-m+1);
        reverse(x.begin(),x.end());
        return x;
    }
    void sq(int n,pl &b,pl &a){
        if(n==1){
            b[0]=3;
            return;
        }
        sq((n+1)/2,b,a);
        pl c,d=a;
        int ii=qp(2,mo-2);
        c.resize(n);
        b.resize(n);
        d.resize(n);
        for(int i=0;i<b.size();i++)
               c[i]=1ll*b[i]*ii%mo;
           for(int i=0;i<b.size();i++)
               b[i]=1ll*b[i]*2%mo;
           d=d*iv(b);
           for(int i=0;i<b.size();i++)
               b[i]=(c[i]+d[i])%mo;
    }
    pl sq(pl x){
        pl y;
        int n=x.size();
        y.resize(n);
        sq(n,y,x);
        y.resize(n);
        return y;
    }
    pl operator -(pl x,pl y){
        int s=max(x.size(),y.size());
        x.resize(s);
        y.resize(s);
        for(int i=0;i<s;i++)
            x[i]=(x[i]-y[i]+mo)%mo;
        return x;
    }
    pl operator %(pl x,pl y){
        int n=(int)x.size()-1,m=(int)y.size()-1;
        if(x.size()<y.size())return x;
        if(!m){
            pl a;
            a.resize(1);
            return a;
        }
        x=x-(x/y)*y;
        x.resize(m);
        return x;
    }
    pl qd(pl x){
        pl y;
        int n=x.size();
        y.resize(n-1);
        //deb(x);
        for(int i=0;i<n-1;i++)
            y[i]=x[i+1]*(i+1)%mo;
        //deb(y);
        return y;
    }
    pl jf(pl x){
        int n=x.size();
        pl y;
        y.resize(n+1);
        for(int i=1;i<=n;i++)
            y[i]=x[i-1]*qp(i,mo-2)%mo;
        return y;
    }
    pl ln(pl x){
        int n=x.size();
        pl y=qd(x),z=iv(x);
        y=y*z;
        y=jf(y);
        y.resize(n);
        return y;
    }
    inline char nc(){
        static char buf[500000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,500000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int rd(){
        char ch=nc();int sum=0;
        while(!(ch>='0'&&ch<='9'))ch=nc();
        while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
        return sum;
    }
    char bf[100];
    void wr(int x){
        if(!x){
            putchar('0');
            putchar(' ');
            return;
        }
        int ct=0;
        while(x){
            bf[++ct]=x%10;
            x/=10;
        }
        for(int i=ct;i;i--)
            putchar(bf[i]+'0');
        putchar('
    ');
    }
    namespace qz{
        ll b[N],ans[N],pp[N];
        pl t;
        void fz(int o,int l,int r,pl &p,pl *a){
            if(l==r){
                a[o].resize(2);
                a[o][0]=(mo-p[l])%mo;
                a[o][1]=1;
                return;
            }
            int md=(l+r)/2;
            fz(o*2,l,md,p,a);
            fz(o*2+1,md+1,r,p,a);
            a[o]=a[o*2]*a[o*2+1];
            //deb(a[o]);
        }
        void ga(int o,int l,int r,pl &ans,pl *a,pl *c){
            if(r-l<=100){
                for(int i=l;i<=r;i++){
                    int r=0;
                    for(int j=c[o].size()-1;~j;j--)
                        r=(1ll*r*pp[i]%mo+c[o][j])%mo;
                    ans[i]=r;
                }
                return;
            }
            if(l==r){
                ans[l]=c[o][0];
                return;
            }
            int md=(l+r)/2;
            c[o*2]=c[o]%a[o*2];
            c[o*2+1]=c[o]%a[o*2+1];
            ga(o*2,l,md,ans,a,c);
            ga(o*2+1,md+1,r,ans,a,c);
        }
        void gt(pl t,pl &ans){
            //while(t.size()<ans.size())t.push_back(0);
            int n=ans.size();
            static pl a[N],b[N];
            for(int i=0;i<n;i++)
                pp[i]=ans[i];
            fz(1,0,n-1,ans,a);
            if(n>=m)b[1]=t%a[1];
            ga(1,0,n-1,ans,a,b);
        }
        void d2(int o,int l,int r,pl &y,pl *a,pl *b){
            if(l==r){
                b[o].resize(1);
                b[o][0]=y[l];
                return;
            }
            int md=(l+r)/2;
            d2(o*2,l,md,y,a,b);
            d2(o*2+1,md+1,r,y,a,b);
            b[o]=b[o*2]*a[o*2+1]+b[o*2+1]*a[o*2];
        }
    };
    pl cz(int n,pl &x,pl &y){
        static pl a[N],b[N];
        qz::fz(1,0,n-1,x,a);
        qz::gt(qd(a[1]),x);
        //deb(x);
        for(int i=0;i<n;i++)
            y[i]=y[i]*qp(x[i],mo-2)%mo;
        //deb(y);
        qz::d2(1,0,n-1,y,a,b);
        return b[1];
    }
    void gt(int n,pl &y,pl x){
        if(n==1){
            y.resize(1);
            y[0]=1;
            return;
        }
        gt((n+1)/2,y,x);
        pl z=x,a;
        z.resize(n);
        y.resize(n);
        a.resize(1);
        a[0]=1;
        y=y*(a-ln(y)+z);
        y.resize(n);
    }
    pl ep(pl x){
        pl y;
        int n=x.size();
        gt(n,y,x);
        return y;
    }
    pl qp(pl x,int y){
        int iv=qp(x[0],mo-2),t=x[0];
        for(int i=0;i<x.size();i++)
            x[i]=x[i]*iv%mo;
        pl r=ln(x);
        for(int i=0;i<r.size();i++)
            r[i]=r[i]*(y%mo)%mo;
        r=ep(r);
        for(int i=0;i<r.size();i++)
            r[i]=r[i]*qp(t,y%(mo-1))%mo;
        return r;
    }
    void put(pl a){
        for(int i=0;i<a.size();i++)
            printf("%lld ",a[i]);
        puts("");
    }
    
    signed main(){
        freopen("w1.out","w",stdout);
        cin>>n>>k;
        pl ss,t,tt;
        ss.resize(k+1);
        ss[0]=9;
        ss[1]=mo-2;
        ss[2]=1;
        ss=sq(ss);
        tt=iv(ss);
        pl v1=ss,v2=t-ss;
        v1[0]=(v1[0]+1)%mo;
        v1[1]=(v1[1]+1)%mo;
        v2[0]=(v2[0]+1)%mo;
        v2[1]=(v2[1]+1)%mo;
        for(int i=0;i<=k;i++){
            v1[i]=qp(2,mo-2)*v1[i]%mo;
            v2[i]=qp(2,mo-2)*v2[i]%mo;
        }
        pl v3=qp(v1,n-1),v4=qp(v2,n-1),v5,v6;
        v5=v3-v4;
        v3=v3*v1;
        v4=v4*v2;
        v6=v3-v4;
        v5=v5*tt;
        v6=v6*tt;
        for(int i=0;i<=k;i++)
            g[i]=(v6[i]+v5[i]*2)%mo;
        for(int i=1;i<=k;i++)
            g[i]=(g[i]-v5[i-1]+mo)%mo;
        for(int i=0;i<=k;i++)
            cout<<(g[i]+v6[i]*2)%mo<<' ';
    }
    View Code

    lg5243

    这道题是个dp。

    这道题构成欧拉回路的条件也可以被写成:从每棵树上选出一条路径,且路径的长度之和>=y

    设f[i][0]表示当前的树的方案数,f[i][1]表示当前的树的方案数的长度和。

    则f[i+j][0]=f[i][0]+f[j][0]

    f[i+j][1]=f[i][0]*f[j][1]+f[j][0]+f[i][1]

    题目上限制的路径长度之和>=y要求我们不能把路径的长度具体的在状态上存起来,有2种处理方法:

    1.容斥:变成<y

    2.在dp的时候,把第一维对y取min,最后取y这一维作为答案即可。

    加一个剪枝即可过此题。

    uoj94

    集合幂级数的一道入门题。

    fwt的几条重要性质:它的直接定义式可以考虑变换的过程求出。它是线性变换(和的fwt=fwt的和)。fwt的逆变换要乘的矩阵就是原变换矩阵的逆矩阵。如果要乘法,则可以fwt后点乘。

    定义子集卷积:f[s|t]+=a[s]*b[t](s&t==0)

    这样子直接做是O(3^n)的

    但是可以使用占位多项式优化。

    设f[i][s],f[i][s]只有当i=bitcount(s)时为a[s],否则为0

    f[x+y][]+=a[x][]*b[y][]

    如果设s的1位数位x,t的为y

    则x+y=bitcount(s|t)等价于s&t=0

    所以可以对f[i][]作高维前缀和(原因后面再讲),再进行点乘。

    这样子时间复杂度是2^n*n^2的

    实际上,发现f[][j],每一个j在做完后是独立的,f[i][]构成了2^n个多项式。

    这带给了一些优美的性质。

    f[][j]的求逆可在2^n*n^2的时间复杂度内完成,对每个多项式求逆即可。

    定义幂级数的导数为$x^S=bitcount(s)x^{s-{t}}$t为任意集合。

    发现这个导数的定义满足导数的性质。但是定义积分是困难的。

    只要作高维前缀和,就可以快速计算导数(就是占位多项式的前一位),同时推出集合幂级数求ln的做法。

    回到原题。设g表示原图的连通生成子图的集合幂级数。h表示导出子图的个数的集合幂级数。

    有h+1=sum g^k/k!(k>1)=e^g

    ln(h+1)=g

    最后要求的:sum i>0 g^k =1/(1-g)(内部的系数已经带入函数了)

    把ln带进,得1/(1-ln(h+1))

    ln就是对占位多项式求ln。时间复杂度2^n*n^2

    #include<bits/stdc++.h>
    using namespace std;
    #define N 21
    #define mo 998244353
    int n,m,f[N][(1<<20)+10],g[N][(1<<20)+10],c[(1<<20)+10],iv[N],a[N*N],b[N*N];
    void fwor(int *a,int l,int r){
        if(l==r)return;
        int md=(l+r)/2;
        fwor(a,l,md);fwor(a,md+1,r);
        for(int i=l;i<=md;i++)
            a[md+i-l+1]=(a[md+i-l+1]+a[i])%mo;
    }
    void ifwor(int *a,int l,int r){
        if(l==r)return;
        int md=(l+r)/2;
        ifwor(a,l,md);ifwor(a,md+1,r);
        for(int i=l;i<=md;i++)
            a[md+i-l+1]=(a[md+i-l+1]-a[i]+mo)%mo;
    }
    int qp(int x,int y){
        int r=1;
        for(;y;y>>=1,x=1ll*x*x%mo)
            if(y&1)r=1ll*r*x%mo;
        return r; 
    }
    signed main(){
        cin>>n>>m;
        for(int i=0;i<m;i++){
            cin>>a[i]>>b[i];
            a[i]--;
            b[i]--;
        }
        for(int i=1;i<(1<<n);i++){
            c[i]=c[i>>1]+(i&1);
            int ct=0;
            for(int j=0;j<m;j++)
                if((i&(1<<a[j]))&&(i&(1<<b[j])))ct++;
            f[c[i]][i]=qp(2,ct);
        }
        for(int i=1;i<=n;i++){
            fwor(f[i],0,(1<<n)-1);
            iv[i]=qp(i,mo-2);
        }
        for(int i=1;i<=n;i++){
            for(int k=0;k<i;k++)
                for(int j=0;j<(1<<n);j++)
                    g[i][j]=(g[i][j]+1ll*k*g[k][j]%mo*f[i-k][j])%mo;
            for(int j=0;j<(1<<n);j++)
                g[i][j]=(f[i][j]+1ll*(mo-iv[i])*g[i][j]%mo)%mo;
        }
        memset(f,0,sizeof(f));
        for(int i=0;i<(1<<n);i++)
            f[0][i]=1;
        for(int i=1;i<=n;i++)
            for(int k=0;k<i;k++)
                for(int j=0;j<(1<<n);j++)
                    f[i][j]=(f[i][j]+1ll*f[k][j]*g[i-k][j]%mo)%mo;
        ifwor(f[n],0,(1<<n)-1);
        cout<<f[n][(1<<n)-1];
    }
    View Code

    uoj310

    #include<bits/stdc++.h>
    using namespace std;
    #define N 4000010
    #define mo 998244353
    #define int long long
    int n,c[N],x,mx,v,le,p3[N],r;
    void fwxo(int *a,int l,int r){
        if(l==r)return;
        int md=(l+r)/2;
        fwxo(a,l,md);fwxo(a,md+1,r);
        for(int i=l;i<=md;i++){
            int c=a[i],d=a[md+i-l+1];
            a[md+i-l+1]=c-d;
            a[i]=c+d;
        }
    }
    int qp(int x,int y){
        int r=1;
        for(;y;y>>=1,x=x*x%mo)
            if(y&1)r=r*x%mo;
        return r;
    }
    signed main(){
        cin>>n;
        for(int i=1;i<=n;i++){
            scanf("%lld",&x);
            c[x]++;
            mx=max(mx,x);
        }
        p3[0]=1;
        for(int i=1;i<N;i++)
            p3[i]=p3[i-1]*(mo-3)%mo;
        v=1;
        while(v<mx)v*=2,le++;
        fwxo(c,0,v-1);
        for(int i=0;i<v;i++)
            r=(r+p3[(c[i]+n)/2])%mo;
        r=r*qp((mo+1)/2,le)%mo;
        if(n&1)r=(mo-r)%mo;
        r=(r-1+mo)%mo;
        cout<<r;
    }
    View Code
  • 相关阅读:
    GitLab 重置认证和添加账号缓存
    PHP 正则匹配IP
    git 删除指定版本
    PostgreSQL 9.2 日期运算
    postgre 已有字段修改为自增
    postgresql 导入导出
    PHP TS 和 NTS 版本选择
    background-image属性的设置
    SQLServer 附加数据库后只读或报错解决方法
    IIS 6.0 发布网站使用教程
  • 原文地址:https://www.cnblogs.com/cszmc2004/p/12826803.html
Copyright © 2011-2022 走看看