• bzoj月赛1805


    题目在最后,FG还不会做,等着$NicoDafaGood$和$Achen$给我讲

    A

    对于每一个质因子建一棵线段树,直接查询就好了

    主要是看到所有数的大小都不是很大,然后质因子最多只有log个,复杂度两个log又是能承受的

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=1e5+7,W=1e5,maxm=2e7+7;
    int Td,n,m;
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    int ok[maxn],prime[maxn],totp,id[maxn];
    void get_p() {
        For(i,2,W) {
            if(!ok[i]) prime[id[i]=++totp]=i;
            For(j,1,totp) {
                if(prime[j]>W/i) break;
                ok[i*prime[j]]=1;
                if(i%prime[j]==0) break;
            }
        }
    }
     
    int sum[maxm],son[maxm][2],tot,ql,qr,qx;
    void bld(int &pos,int l,int r) {
        if(!pos) pos=++tot;
        sum[pos]+=qx;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(ql<=mid) bld(lc,l,mid);
        else bld(rc,mid+1,r);
    }
     
    int q(int pos,int l,int r) {
        if(!pos) return 0;
        if(l>=ql&&r<=qr) return sum[pos];
        int mid=(l+r)>>1,rs=0;
        if(ql<=mid) rs+=q(lc,l,mid);
        if(qr>mid) rs+=q(rc,mid+1,r);
        return rs;
    }
     
    void get_bld(int p,int x) {
        ql=qr=p;
        For(i,1,totp) {
            if(prime[i]>x/prime[i]) break;
            if(x%prime[i]) continue;
            qx=0; while(x%prime[i]==0) x/=prime[i],qx++;
            bld(i,1,n);
        }
        if(x>1) {qx=1;bld(id[x],1,n);}
    }
     
    bool check(int x) {
        For(i,1,totp) {
            if(prime[i]>x/prime[i]) break;
            if(x%prime[i]) continue;
            qx=0; while(x%prime[i]==0) x/=prime[i],qx++;
            if(qx>q(i,1,n)) return 0;
        }
        if(x>1&&q(id[x],1,n)<1) return 0;
        return 1;
    }
     
    void clear() {
        For(i,1,tot) sum[i]=son[i][0]=son[i][1]=0;
        tot=totp;
    }
     
    int main() {
        read(Td); int x;
        get_p();
        while(Td--) {
            read(n); read(m);
            clear();
            For(i,1,n) read(x),get_bld(i,x);
            For(i,1,m) {
                read(ql); read(qr); read(x);
                if(check(x)) printf("Yes
    ");
                else printf("No
    ");
            }
        }
        return 0;
    }
    

      

    B

    一开始一直在想用最小的替换最大的,然后就在纠结什么前20种我选了多少,前21种我选了多少...,选了的不能替换啊

    然后复杂度也不对,想起来也复杂

    当时想这道题的时候想到了什么我从$(i,j)$走到了$(i+1,j)$,所以$(i,j+1),(i,j+2),...,(i,m)$原本是可能走到的,现在就一定不能走到了

    也就是说我们现在就可以拿这一段的来替换一些什么的了

    我们把我们走过的其中$K$个不要,然后拿没走过的$K$个来填。

    预处理出这样的一段的从大到小排序后的前缀和,这样就可以每次贪心地dp了

    $dp[i][j][k][t]$就是这样定义的状态,我现在让我走过的$k$个没有算,然后在外面拿了$t$个了,复杂度是$nmK^3$

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=53,maxk=23,INF=0x3f3f3f3f;
    int Td,n,m,K,a[maxn][maxn];
    int dp[maxn][maxn][maxk][maxk],g[2][maxn][maxn][maxn];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    void get_g() {
        int p;
        For(i,1,n) For(j,1,m) {
            p=m-j+1;
            For(k,1,p) g[0][i][j][k]=a[i][j+k-1];
            sort(g[0][i][j]+1,g[0][i][j]+p+1,greater<int>());
            For(k,2,p) g[0][i][j][k]+=g[0][i][j][k-1];
        }
        For(i,1,n) For(j,1,m) {
            p=n-i+1;
            For(k,1,p) g[1][i][j][k]=a[i+k-1][j];
            sort(g[1][i][j]+1,g[1][i][j]+p+1,greater<int>());
            For(k,2,p) g[1][i][j][k]+=g[1][i][j][k-1];
        }
    }
     
    void get_max(int &x,int y) {if(y>x) x=y;}
     
    int get_dp() {
        int p,x;
        dp[1][1][0][0]=a[1][1]; dp[1][1][1][0]=0;
        For(i,1,n) For(j,1,m) For(k,0,K) For(t,0,K) {
            if((x=dp[i][j][k][t])==-INF) continue;
            if(i<n) {
                p=min(K-t,m-j);
                x+=a[i+1][j];
                For(r,0,p) get_max(dp[i+1][j][k][t+r],x+g[0][i][j+1][r]);
                x-=a[i+1][j];
                For(r,0,p) get_max(dp[i+1][j][k+1][t+r],x+g[0][i][j+1][r]);
            }
            if(j<m) {
                p=min(K-t,n-i);
                x+=a[i][j+1];
                For(r,0,p) get_max(dp[i][j+1][k][t+r],x+g[1][i+1][j][r]);
                x-=a[i][j+1];
                For(r,0,p) get_max(dp[i][j+1][k+1][t+r],x+g[1][i+1][j][r]);
            }
        }
        int rs=-INF;
        For(k,0,K) get_max(rs,dp[n][m][k][k]);
        return rs;
    }
     
    void clear() {
        For(i,1,n) For(j,1,m) For(k,0,K) For(t,0,K) dp[i][j][k][t]=-INF;
        For(i,1,n) For(j,1,n) For(k,1,n) g[0][i][j][k]=g[1][i][j][k]=-INF;
    }
     
    int main() {
        read(Td);
        while(Td--) {
            read(n); read(m); read(K);
            clear();
            For(i,1,n) For(j,1,m) read(a[i][j]);
            get_g();
            printf("%d
    ",get_dp());
        }
        return 0;
    }
    

      

     C

    这道题把我恶心的,一开始我暴力直接枚举根,然后枚举方案数怎么分配到左右儿子上,写起来真难受,跑得还慢。

    关键是sb如我还不小心把B的代码交上去了T了几发,sb如我还一直没有注释掉cerr<<clock(),RE了好几发

    然后心里一横就重构代码,看别人的AC代码都是1k多,我将近3k,然后就突然想起了什么……

    任何一个子树的方案数一定是$k$的约数啊,$k$的约数个数是$sqrt{n}$级别的啊,如果数据水一点,或许我可以水过

    于是我开了个set,$G[i]$存的是,大小为$i$的树的可能的方案数,并且是$k$的约数,然后就水过去了

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<set>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=4000+7,maxm=1e5+7,W=4000;
    const ll INF=1e9;
    ll n,K,C[maxn][maxn];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    struct Node{
        ll x,y,z;
        Node(ll x,ll y,ll z):x(x),y(y),z(z){}
        bool operator < (const Node& b) const{return x<b.x;}
    };
    set<Node> G[maxm];
    set<Node>::iterator it1,it2;
     
    void get_ans(ll n,ll k,int p) {
        if(!n) return;
        it1=G[n].find(Node(k,0,0));
        ll x=it1->y,y=it1->z;
        printf("%lld ",x+p+1);
        get_ans(x,y,p);
        get_ans(n-1-x,k/C[n-1][x]/y,x+p+1);
    }
     
    bool solve() {
        if(K==1) return printf("1
    1
    "),1;
        ll x,y; G[1].insert(Node(1,0,1)); G[0].insert(Node(1,0,1));
        For(i,2,W) {
            n=i;
            For(j,0,i-1) {
                int k=n-j-1;
                if(C[i-1][j]<=0||K%C[i-1][j]!=0) continue;
                for(it1=G[j].begin();it1!=G[j].end();++it1) {
                    x=it1->x; if(x>K/C[i-1][j]) break;
                    for(it2=G[k].begin();it2!=G[k].end();++it2) {
                        y=it2->x;
                        if(x*y>K/C[i-1][j]) break;
                        if(K%(x*y*C[i-1][j])) continue;
                        G[i].insert(Node(x*y*C[i-1][j],j,x));
                    }
                }
            }
            if(G[i].find(Node(K,0,0))!=G[i].end()) break;
        }
        if(G[n].find(Node(K,0,0))==G[n].end()) return 0;
        printf("%lld
    ",n);
        get_ans(n,K,0);
        printf("
    ");
        return 1;
    }
     
    int main() {
        read(K); C[0][0]=1;
        For(i,1,W) {
            C[i][0]=1;
            For(j,1,W) {
                C[i][j]=C[i-1][j]+C[i-1][j-1];
                if(C[i][j]>K||C[i][j]<0) C[i][j]=-INF;
            }
        }
        if(!solve()) printf("-1
    ");
    //  cerr<<clock()<<"
    ";
        return 0;
    }
    

    暴力枚举根的代码:

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<map>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=4000+7,maxm=1e5+7,W=4000;
    const ll INF=1e9;
    ll n,k,C[maxn][maxn],tot,r[maxn];
    int son[maxm][2],sum[maxm];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    ll f(ll x) {
        ll t=sqrt(x);
        For(i,2,t) if(x%i==0) return i;
        return x;
    }
     
    ll g(ll x,ll l,ll r) {
        For(i,l,r) if(x%i==0) return i;
        return -1;
    }
     
    const ll Bs=31,U=(1LL<<Bs)-1;
    ll pr(ll x,ll y) {return (x<<Bs)+y;}
    ll fi(ll x) {return x>>Bs;}
    ll se(ll x) {return x&U;}
    map<int,ll> H[maxn];
    inline bool check(int pos,ll n,ll k) {
        lc=rc=0; sum[pos]=n;
        if(n<=1) return k==1;
        if(r[n]<k||H[n][k]==-1) return 0;
        ll x,y,z; int bot=tot;
        if(H[n][k]) {
            x=fi(H[n][k]); y=se(H[n][k]);
            lc=++tot; rc=++tot;
            check(lc,x,y);
            check(rc,n-1-x,k/C[n-1][x]/y);
            return 1;
        }
        For(i,0,(n-1)>>1) {
            if(C[n-1][i]<0) break;
            if(k%C[n-1][i]) continue;
            x=k/C[n-1][i]; z=0; y=sqrt(x);
            lc=++tot; rc=++tot;
            while(~(z=g(x,z+1,y))) {
                if(check(lc,i,z)&&check(rc,n-1-i,x/z)) 
                    return H[n][k]=pr(n-1-i,x/z),1;
                if(z!=x/z&&check(lc,i,x/z)&&check(rc,n-1-i,z)) 
                    return H[n][k]=pr(n-1-i,z),1;
            }
            tot=bot;
        }
        lc=rc=0; H[n][k]=-1;
        return 0;
    }
     
    void get_ans(int pos,int p) {
        if(pos==0||sum[pos]==0) return;
        printf("%d ",p+sum[lc]+1);
        get_ans(lc,p);
        get_ans(rc,p+sum[lc]+1);
    }
     
    bool solve() {
        r[1]=r[0]=1;
        For(i,1,W) {
            For(j,0,i>>1) r[i]=max(r[i],r[j]*r[i-j-1]*C[i-1][j]);
            if(r[i]<k) continue;
    //      if(i%100==0) cerr<<i<<": "<<clock()<<"
    ";
            tot=1; n=i;
            if(!check(1,n,k)) continue;
            printf("%lld
    ",n);
            get_ans(1,0);
            printf("
    ");
            return 1;
        }
        return 0;
    }
     
    int main() {
        read(k); C[0][0]=1;
        For(i,1,W) {
            C[i][0]=1;
            For(j,1,W) {
                C[i][j]=C[i-1][j]+C[i-1][j-1];
                if(C[i][j]>k||C[i][j]<0) C[i][j]=-INF;
            }
        }
        if(!solve()) printf("-1
    ");
    \  cerr<<clock()<<"
    ";
        return 0;
    }
    
    

      

    D

    一直想着二分二分,但是不知道怎么判断,记得以前好像遇到过类似的,似乎是Achen讲过的什么题,就是给每个值随机一个权值

    这样子我们就可以在主席树上二分啦

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll unsigned long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=4e5+7,maxt=23,TOT=2e5+2,W=19,maxm=2e7+7;
    int Td,n,m,a[maxn];
    ll val[maxn],sum[maxn];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    int fir[maxn],nxt[2*maxn],to[2*maxn],e=0;
    void add(int x,int y) {
        to[++e]=y;nxt[e]=fir[x];fir[x]=e;
        to[++e]=x;nxt[e]=fir[y];fir[y]=e;
    }
     
    ll num[maxm],tot,ql,qr,qx;
    int son[maxm][2];
     
    void bld(int& pos,int last,int l,int r) {
        if(!pos) pos=++tot;
        num[pos]=num[last]^qx;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(ql<=mid) rc=son[last][1],bld(lc,son[last][0],l,mid);
        else lc=son[last][0],bld(rc,son[last][1],mid+1,r);
    }
     
    int fa[maxn][maxt],dep[maxn];
    void dfs(int pos,int f) {
        ql=qr=a[pos]; qx=val[a[pos]]; bld(pos,f,1,TOT);
        int y,z; dep[pos]=dep[f]+1;
        fa[pos][0]=f; For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1];
        for(y=fir[pos];y;y=nxt[y]) {
            if((z=to[y])==f) continue;
            dfs(z,pos);
        }
    }
     
    int get_lca(int x,int y) {
        if(dep[x]!=dep[y]) {
            if(dep[x]<dep[y]) swap(x,y);
            Rep(i,W,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
        }
        if(x==y) return x;
        Rep(i,W,0) if(fa[x][i]!=fa[y][i]) {
            x=fa[x][i]; y=fa[y][i];
        }
        return fa[x][0];
    }
     
    int q(int pos,int p,int l,int r) {
        if(l==r) return l;
        int mid=(l+r)>>1;ll o=(qx>=l&&qx<=mid)? val[qx]:0;
        if((num[lc]^num[son[p][0]]^o)==(sum[mid]^sum[l-1])) 
            return q(rc,son[p][1],mid+1,r);
        return q(lc,son[p][0],l,mid);
    }
     
    int Yth(int x,int y) {
        int lca=get_lca(x,y);
        qx=a[lca];
        return q(x,y,1,TOT);
    }
     
    void clear() {
        For(i,1,n) fir[i]=0;
        For(i,1,tot) num[i]=son[i][0]=son[i][1]=0;
        e=0;
    }
     
    int main() {
        val[1]=1;
        For(i,2,TOT) val[i]=val[i-1]*(ll)233;
        For(i,1,TOT) sum[i]=sum[i-1]^val[i];
        read(Td); int x,y;
        while(Td--) {
            clear();
            read(n); read(m); tot=n;
            For(i,1,n) read(a[i]);
            For(i,1,n-1) {
                read(x); read(y);
                add(x,y);
            }
            dfs(1,0);
            For(i,1,m) {
                read(x); read(y);
                printf("%d
    ",Yth(x,y));
            }
        }
        return 0;
    }

      

    E

    首先,换一种理解题意的方式,每两个点$(x,y)$之间有一条权值为$a[x]$^$a[y]$的边,

    让你在这$frac{n(n-1)}{2}$条边中选$n$条,使得每个连通块不是一个环就是一个基环外向树,在此条件下,选的边的权值和最小

    $n=2$的情况特殊处理

    $n$这么大,肯定不能直接把所有边拿出来,所以很容易想到在字典树上贪心

    对于字典树上的一个点,我们考虑lca在这里的边

    那么怎么贪心呢,我们知道,对于一个$n$个点的连通块($n>2$),肯定是$n$条边,也就是说

    如果我们在这里可以连边,就不要跑到父亲那里去连边

    换句话说,字典树上,从底向上,能连边就连边,这样一定是最优的

    这样分为几种情况讨论:

    1、左右儿子大小都$geq 3$,因为我们在做底下的时候也是贪心,所以左右两个都是一堆环和基环外向树,两个之间没法连边

    2、一个$leq 2$,一个$geq 3$,一个是链,一个是一堆环和基环外向树,那么连一条边把链接到一个基环外向树上就可以了,贪心选择最小的那个

    3、一个$=2$,一个$leq 2$,连两条权值最小的边,形成环

    4、两个都是$=1$,直接连一条边连成链就好了

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=1e6+7,W=30;
    ll Td,n,a[maxn];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    ll solve(ll p,int l,int r) {
        if(l==r||a[l]==a[r]) return 0;
        if(l==r-1) return a[l]^a[r];
        int mid=l; ll rs=0,min1=1<<W,min2=1<<W;
        while(mid<=r&&((a[mid]>>p)&1)==0) mid++;
        if(mid>l) rs+=solve(p-1,l,mid-1);
        if(mid<=r) rs+=solve(p-1,mid,r);
        if(mid==l||mid>r||(mid-l>=3&&r-mid+1>=3)) return rs;
        For(i,l,mid-1) For(j,mid,r) {
            if((a[i]^a[j])<=min1) min2=min1,min1=a[i]^a[j];
            else if ((a[i]^a[j])<min2) min2=a[i]^a[j];
        }
        if(mid-l>2||r-mid+1>2) rs+=min1;
        else rs+=min1+min2;
        return rs;
    }
     
    int main() {
        read(Td);
        while(Td--) {
            read(n);
            For(i,1,n) read(a[i]);
            sort(a+1,a+n+1);
            printf("%lld
    ",solve(W,1,n));
        }
        return 0;
    }
    

      

    H

    不会点分治也不想写lct的蒟蒻手把手教你怎么水过这道题

    首先题目暗示很明显了,你可以把它颜色当成随机出来的,也就是说相同颜色的很少。

    而我们知道,对于一个回文路径,它的两端首先颜色要相同,所以回文路径也很少。

    如果它两端颜色相同了,还需要满足什么条件呢?

    把两端删掉后是回文路径,或者为空

    那么我们把颜色相同的点对全都拿出来,然后按照路径长度从小到大排序,然后用一个set来存一个点可以与哪些点构成点对可以满足回文路径

    然后我判断当前这个点对$(x,y)$的时候,我就直接看$(px,py)$是不是回文路径就可以了,$px$和$py$是$x$和$y$分别往对方走一步后到达的点

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<set>
    #include<vector>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=2e5+7,maxt=23,W=19,maxm=2e6+7;
    int Td,n,tot;
     
    struct Node{
        int x,y,len,lca;
        Node(){}
        Node(int x,int y,int len,int lca):x(x),y(y),len(len),lca(lca){}
        bool operator < (const Node& b) const{return len<b.len;}
    }node[maxm];
     
    set<int> G[maxn];
    set<int>::iterator it;
    vector<int> H[maxn];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    int fir[maxn],nxt[2*maxn],to[2*maxn],e=0;
    void add(int x,int y) {
        to[++e]=y;nxt[e]=fir[x];fir[x]=e;
        to[++e]=x;nxt[e]=fir[y];fir[y]=e;
    }
     
    int dep[maxn],fa[maxn][maxt];
    void dfs(int pos,int f) {
        fa[pos][0]=f; dep[pos]=dep[f]+1; int y,z;
        For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1];
        for(y=fir[pos];y;y=nxt[y]) {
            if((z=to[y])==f) continue;
            dfs(z,pos);
        }
    }
     
    int get_lca(int x,int y) {
        if(dep[x]!=dep[y]) {
            if(dep[x]<dep[y]) swap(x,y);
            Rep(i,W,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
        }
        if(x==y) return x;
        Rep(i,W,0) if(fa[x][i]!=fa[y][i]) {
            x=fa[x][i]; y=fa[y][i];
        }
        return fa[x][0];
    }
     
    int get_f(int x,int y) {
        Rep(i,W,0) if(y>=(1<<i)) {
            x=fa[x][i]; y-=(1<<i);
        }
        return x;
    }
     
    void get_insert(int x,int y) {
        int lca=get_lca(x,y);
        node[++tot]=Node(x,y,dep[x]+dep[y]-2*dep[lca]+1,lca);
    }
     
    ll solve() {
        ll rs=n; int x,y,lca,px,py;
        For(i,1,n) G[i].insert(i);
        For(i,1,tot) {
            x=node[i].x; y=node[i].y; lca=node[i].lca;
            if(lca!=x) px=fa[x][0]; else px=get_f(y,dep[y]-dep[x]-1);
            if(lca!=y) py=fa[y][0]; else py=get_f(x,dep[x]-dep[y]-1);
            if((px==y&&py==x)||G[px].find(py)!=G[px].end()) {
                rs++;
                G[x].insert(y); G[y].insert(x);
            }
        }
        return rs;
    }
     
    void clear() {
        For(i,1,n) H[i].clear(),G[i].clear(),fir[i]=0;
        tot=e=0;
    }
     
    int main() {
        read(Td); int x,y;
        while(Td--) {
            clear();
            read(n);
            For(i,1,n) read(x),H[x].push_back(i);
            For(i,1,n-1) {
                read(x); read(y);
                add(x,y);
            }
            dfs(1,0);
            For(i,1,n) if((x=H[i].size())>1) {
                For(j,0,x-2) For(k,j+1,x-1) 
                    get_insert(H[i][j],H[i][k]);
            }
            sort(node+1,node+tot+1);
            printf("%lld
    ",solve());
        }
        return 0;
    }
    

      

    I

    考虑一个矩形作为三个矩形的交的时候有1的贡献,为了不重复计算这个矩形,我们可以在$(xl,yl)$这里计算矩形$((xl,yl),(xr,yr))$的贡献

    然后差分及前缀和算出一个格子有$a,b,c,d$四种的矩形多少个

    $d$指这个格子在哪些矩形的左上角

    $b$是这个格子在哪些矩形的上边界(但不是左上角)

    $c$是这个格子在哪些矩形的左边界(但不是左上角)

    $a$是这个格子在哪些矩形内部但不在上边界或者左边界

    假如说一个格子,各有$a,b,c,d$四种矩形$A,B,C,D$个

    那么贡献分为4类,以下$fi(X)$是在$X$中选$i$个的方案数

    1、有3个$d$,贡献是$f3(D)$

    2、有2个$d$,贡献是$f2(D) * (A+B+C)$

    3、有1个$d$,贡献是$D * (A*B+B*C+C*A+f2(A)+f2(B)+f2(C))$

    4、有0个$d$,一定要同时有$b$和$c$,贡献是$A*B*C+f2(B)*C+f2(C)*B$

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=2e5+7,maxm=1000+7,W=1000;
    ll Td,n,a[maxm][maxm],d[maxm][maxm],b[maxm][maxm],c[maxm][maxm];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    void get_add(int xl,int yl,int xr,int yr) {
        a[xl+1][yl+1]++; a[xl+1][yr+1]--; a[xr+1][yl+1]--; a[xr+1][yr+1]++;
        b[xl][yl+1]++; b[xl][yr+1]--;
        c[xl+1][yl]++; c[xr+1][yl]--;
        d[xl][yl]++;
    }
     
    void get_sum() {
        For(i,1,W) For(j,1,W) a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
        For(i,1,W) For(j,1,W) b[i][j]+=b[i][j-1],c[i][j]+=c[i-1][j];
    }
     
    ll f2(ll x) {return x*(x-1)/2;}
    ll f3(ll x) {return x*(x-1)*(x-2)/6;}
     
    ll get_ans() {
        ll rs=0,A,B,C,D;
        For(i,1,W) For(j,1,W) {
            A=a[i][j]; B=b[i][j]; C=c[i][j]; D=d[i][j];
            rs+=D*(A*B+A*C+B*C+f2(A)+f2(B)+f2(C)); //D+...
            rs+=f2(D)*(A+B+C); //2D+...
            rs+=f3(D); //3D
            rs+=A*B*C+f2(B)*C+f2(C)*B; //0D
        }
        return rs;
    }
     
    void clear() {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        memset(d,0,sizeof(d));
    }
     
    int main() {
        read(Td); int xl,yl,xr,yr;
        while(Td--) {
            clear();
            read(n);
            For(i,1,n) {
                read(xl); read(yl); read(xr); read(yr);
                get_add(xl,yl,xr,yr);
            }
            get_sum();
            printf("%lld
    ",get_ans());
        }
        return 0;
    }
    

      

    ud20180608:

    昨天Achen退役回家了。

    然后我突然发现,机房真的好荒凉啊。

    前几天一个人在203的时候,一开始也有一种荒凉和孤单的感觉,感觉有点怕。

    但是那个时候,我还可以在QQ上问Achen题,聊天、吐槽什么的。

    现在,我和NicoDafaGood两人在机房的两端,各自做题,感觉机房特别安静,也找不到人聊天吐槽了

    就觉得好怕,好难受啊。

    我越来越感受到yyh学长当初的感觉了

    NicoDafaGood说我不能慌啊,但是我真的觉得,好怕。

  • 相关阅读:
    Elasticsearch 客户端TransportClient vs RestClient
    MySQL(三)——MySQL45题
    MySQL(二)——其他基础功能
    MySQL(一)——CRUD语句
    JVM(十)——类的加载与加载器
    JVM(九)——字节码指令集
    每日总结
    《构建之法》读后感(三)
    《构建之法》读后感(二)
    《构建之法》读后感(一)
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/9134794.html
Copyright © 2020-2023  润新知