• csp-s2019 总结


    D1

    T1

    不谈

    T2

    可以先考虑在序列上的情况,考虑对于一个 ),设 (f[i]) 表示以 i 结尾的答案是多少,有:(f[i]=f[j]+1),其中 j 是可以与 i 匹配的前一个位置。然后可以用栈来维护,然后再拓展到树上就行了。。。然后就是记录一下操作然后回朔就行了。。。

    T3

    写了再写

    D2

    T1

    其实是挺明显的容斥题目的,然后在考场上时我又想过容斥但不知道为什么不了了之(算了,就当是我右边的人的锅^^)。

    考虑 dp,首先可以知道肯定只有一个食材是超了的,于是可以枚举这个食材,然后可以再以横行为阶段进行 dp。。。

    然后可以设 (f[i][j][h][k]) 表示第 i 列超了,选到第 j 行,i 列有 h 个,其他有 k 个的方案数。。

    然后可以发现我们不用管具体h,k是多少,只用关心他们的差就行了,然后就可以优化状态。。。

    然后到最后一行时,k>0 的情况即是要求的,然后中间可能有负数,然后平移一下就好。。

    T2

    呃,首先可以发现一个结论:最优解同时一定也是所有合法解中最后一段的和最小的解。

    然后就是求:(f_i=min{S_i-S_jmid S_i-S_jgeq f_j}) (f_i) 表示i结尾的最后一段的长度。

    然后一看就可以单调队列优化。。。按 (s_i+f_i) 来维护单调队列就行了。。。

    所以我当时为什么用这个性质写了个 (n^2) dp?

    然后100分要写高精,订正时可以用__int128水,然后会炸空间,然后可以最后再求和。。

    T3

    树的重心的性质:

    1.删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心;(我觉得这个性质应该是比定义要直观具体好用的)

    2.树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;

    3.两个树通过一条边合并,新的重心在原树两个重心的路径上;

    4.树删除或添加一个叶子节点,重心最多只移动一条边;

    然后首先这题肯定是要对每个点分别求贡献的。。

    首先我们可以求出整体的重心,然后以它为根。。

    考虑对于一个非根的点 x,删什么它会有贡献。。

    首先肯定不可以删它子树的边,这个易证。。

    然后考虑如果删非它子树的边:

    (S) 表示删掉一条边后的另一棵树的大小。。(g_x) 表示 x 的最大儿子的大小。。

    然后有:

    [2 imes g_xleq n-S\ 2 imes (n-sz_x-S)leq n-S ]

    然后整理可得:

    [n-2 imes sz_xleq Sleq n-2 imes g_x ]

    然后这就可以用树状数组区间求值了。。

    然后考虑如何维护这个树状数组。。

    考虑删的边和 x 的关系:

    1.是 x 的儿子,这种情况即使S满足要求也不算,要再减去。。

    2.是 x 的祖先,这种情况 (S=n-sz_y),可以 dfs 时维护。

    3.和 x 没啥关系,这种情况 (S=sz_y),这种情况可以一开始就add进去。。

    然后 dfs 的步骤:

    这个dfs时我觉得看做边为主体要清晰些。。。

    code:

    void dfs2(int x,int fa){
    	add(0,n-sz[x],1);维护 2 的情况
    	if(x!=rt){
    		ans+=x*ask(0,n-2*g[x]);
    		ans-=x*ask(0,n-2*sz[x]-1);把 1,2,3的情况都算上去
    		ans+=x*ask(1,n-2*g[x]);
    		ans-=x*ask(1,n-2*sz[x]-1);
    		if(z[fa]) z[x]=1;
    		ans+=rt*(sz[x]<=n-2*sz[z[x]?v:u]);
    	}
    	add(0,sz[x],-1);维护1,因为接下来要dfs的就是x的儿子了,S就要变
    	add(1,sz[x],1);
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y==fa) continue;
    		dfs2(y,x);
    	}
    	add(0,sz[x],1);要回溯时这条边就从2成了3。。
    	add(0,n-sz[x],-1);
    	if(x!=rt){
            ans-=x*ask(1,n-2*g[x]);
            ans+=x*ask(1,n-2*sz[x]-1);用可减性减去1的情况。。
        }
    }
    

    然后就是考虑x=rt的情况:

    设rt的儿子中子树最大的节点为u,次大的节点为v。

    若割掉的边在u的子树中,则需要满足:(2 imes s_v le n – S)

    即:(S le n – 2s_v)

    否则,需要满足:(2 imes s_u le n – S)

    即:(S le n – 2s_u)

    可以在 dfs 的时候直接维护。

    这题总体思路和天天爱跑步有点类似,都是推式子然后在树上维护求答案,然后都利用了有些数据在树上的可减性来求子树中的值。。

    本题应该也可以算是换根吧?

    做这种题一定要把所有可能情况讨论清楚,然后再去处理。。。

    code:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=3e5+7;
    template <class I>
    inline void read(I &x){
        int f=1;
        char c;
        for(c=getchar();c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
        for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+(c&15),c=getchar());
        x*=f;
    }
    int head[N],nxt[2*N],ver[2*N],tot=1,n,g[N];
    int sz[N],rt,t[2][N],ans,u,v;
    bool z[N];
    inline void add(int x,int y){
    	ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
    }
    inline void add(int o,int x,int v){
    	x++;
    	while(x<=n+1){
    		t[o][x]+=v;
    		x+=x&-x;
    	}
    }
    inline int ask(int o,int x){
    	int res=0;
    	x++;
    	while(x){
    		res+=t[o][x];
    		x-=x&-x;
    	}
    	return res;
    }
    void dfs(int x,int fa){
    	sz[x]=1,g[x]=0;
    	bool fg=1;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y==fa) continue;
    		dfs(y,x);
    		sz[x]+=sz[y];
    		g[x]=max(g[x],sz[y]);
    		if(g[x]>n/2) fg=0;
    	}
    	if(n-sz[x]>n/2) fg=0;
    	if(fg) rt=x;
    }
    void dfs2(int x,int fa){
    	add(0,n-sz[x],1);
    	if(x!=rt){
    		ans+=x*ask(0,n-2*g[x]);
    		ans-=x*ask(0,n-2*sz[x]-1);
    		ans+=x*ask(1,n-2*g[x]);
    		ans-=x*ask(1,n-2*sz[x]-1);
    		if(z[fa]) z[x]=1;
    		ans+=rt*(sz[x]<=n-2*sz[z[x]?v:u]);
    	}
    	add(0,sz[x],-1);
    	add(1,sz[x],1);
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y==fa) continue;
    		dfs2(y,x);
    	}
    	add(0,sz[x],1);
    	add(0,n-sz[x],-1);
    	if(x!=rt){
            ans-=x*ask(1,n-2*g[x]);
            ans+=x*ask(1,n-2*sz[x]-1);
        }
    }
    void solve(){
    	read(n);
    	ans=0;
    	for(int i=1;i<=n;i++)
    		head[i]=sz[i]=g[i]=0;
    	tot=1;
    	for(int i=1;i<n;i++){
    		int x,y;
    		read(x),read(y);
    		add(x,y),add(y,x);
    	}
    	dfs(1,0);
    	dfs(rt,0);
    	u=0,v=0;
    	for(int i=head[rt];i;i=nxt[i]){
            int x=ver[i];
            if(sz[x]>sz[v]) v=x;
            if(sz[v]>sz[u]) swap(u,v);
        }
        for(int i=1;i<=n+1;i++) t[0][i]=t[1][i]=0;
        for(int i=1;i<=n;i++) add(0,sz[i],1),z[i]=0;
        z[u]=1;
    	dfs2(rt,0);
    	printf("%lld
    ",ans);
    }
    signed main(){
    	int T;	
    	read(T);
    	while(T--){
    		solve();
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    MySQL性能优化
    性能测试结果分析
    TFS使用之代码管理
    新博开通,近期将推出系列博客之测试工具篇!
    绝对受用的TFS操作指南
    2008 &amp; 2005 TFS 安装心得及安装时遇到的问题!
    测试用例如何进行评审?
    cordova H5打包APK关键几点记录
    Silverlight+wcf 结合窗体验证演示
    两道js笔试题
  • 原文地址:https://www.cnblogs.com/Hikigaya/p/11891169.html
Copyright © 2020-2023  润新知