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\n");
            else printf("No\n");
        }
    }
    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\n",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\n1\n"),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",n);
    get_ans(n,K,0);
    printf("\n");
    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\n");
//  cerr<<clock()<<"\n";
    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()<<"\n";
        tot=1; n=i;
        if(!check(1,n,k)) continue;
        printf("%lld\n",n);
        get_ans(1,0);
        printf("\n");
        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\n");
\\  cerr<<clock()<<"\n";
    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\n",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\n",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\n",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\n",get_ans());
    }
    return 0;
}

  

 

 

 

 

 

ud20180608:

昨天Achen退役回家了。

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

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

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

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

就觉得好怕,好难受啊。

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

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

posted @ 2018-06-04 19:26  shixinyi  阅读(222)  评论(0编辑  收藏  举报