• 3.29 考试


    3.29

    今天考出了这次集训到今天的历史最低排名,值得反思,把解题报告先写一写。


    A

    题意:给一颗(n(le 100000))个点的有根树,初始时每个叶子节点有三个状态:(-1,0,1)(-1)表示未确定状态,(0)表示这个点属于(A)(1)表示属于(B)(A)(B)轮流行动,选择一个(-1)状态的叶子节点,标记为自己的状态。非叶子节点的状态是它儿子中出现次数较多的一种(保证儿子是奇数)。两人均采取最优策略。求(A)先手是否必胜,若必胜,求出所有保证必胜的第一步可以选择的节点。


    每个子树可以视作一个游戏,然后合并到根。

    每个子树可以有三个状态(A)必胜,(B)必胜与先手必胜。

    转移的时候,每个人先去选先手必胜的儿子子树,然后可以得到这个点的状态。

    这样就解决了第一问。

    第二问需要分情况讨论

    如果整棵树都是(A)必胜,显然每个未确定的叶子节点都可以

    否则整颗树是先手必胜,我们进子树去查看状态

    如果一个子树是先手必胜就进去,知道进入一个未确定的叶子节点把它加入(未确定的叶子节点就是先手必胜)

    如果一个子树是(B)必胜,其实也是有情况可以选择的,如果我们可以选一个点把这个子树变成先手必胜的话,那么相当于逼(B)去走这一步。什么样子的子树是(B)必胜可以转换成先手必胜呢?其实也很简单,(A)必胜的儿子比​(B)必胜的儿子少一个就是条件。


    Code:

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    const int N=1e5+10;
    #define ll long long
    template <class T>
    void read(T &x)
    {
    	int f=0;x=0;char c=getchar();
    	while(!isdigit(c)) f|=c=='-',c=getchar();
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
        if(f) x=-x;
    }
    int head[N],to[N],Next[N],cnt;
    void add(int u,int v)
    {
    	to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
    }
    int n,yuy[N],s[N],col[N],tot;
    void dfs(int now)
    {
    	if(!head[now]) return;
    	for(int i=head[now];i;i=Next[i])
    	{
    		int v=to[i];
    		dfs(v);
    		if(col[v]==1) --yuy[now];
    		else if(col[v]==0) ++yuy[now];
    	}
    	if(yuy[now]>0) col[now]=0;
    	else if(yuy[now]==0) col[now]=-1;
    	else col[now]=1;
    }
    void dfs1(int now)
    {
    	if(!head[now]&&col[now]==-1) {s[++tot]=now;return;}
    	for(int i=head[now];i;i=Next[i])
    	{
    		int v=to[i];
    		if(col[v]==-1||(col[v]==1&&yuy[v]==-1))
    			dfs1(v);
    	}
    }
    void work()
    {
    	memset(head,0,sizeof head);
    	memset(yuy,0,sizeof yuy);
    	cnt=0;
    	read(n);
    	for(int f,i=1;i<=n;i++) read(f),add(f,i);
    	for(int i=1;i<=n;i++) read(col[i]);
    	dfs(1);
    	if(col[1]==1){puts("-1");return;}
    	else if(col[1]==0)
    	{
    		int ct=0;
    		for(int i=1;i<=n;i++) ct+=!head[i]&&col[i]==-1;
    		printf("%d ",ct);
    		for(int i=1;i<=n;i++)
    			if(!head[i]&&col[i]==-1)
    				printf("%d ",i);
    		puts("");
    		return;
    	}
    	tot=0;
    	dfs1(1);
    	std::sort(s+1,s+1+tot);
    	printf("%d ",tot);
    	for(int i=1;i<=tot;i++) printf("%d ",s[i]);
    	puts("");
    }
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        int T;
        read(T);
        while(T--) work();
    	return 0;
    }
    

    B

    题意:有一个长为(n(n=2^k,kle 20,kin ))的正整数序列(a_i),且有

    [b_i=sum_{0le j<n}((bitcount((i or j)xor i)+1)mod 2)a_i ]

    现在给你(b),请还原序列(a)


    这个题做法比较多,说一种我会了的

    上面这个式子其实等价于

    [b_i=sum_j[bitcount((sim i)& j)&1=0]a_j ]

    后面的取反把(b)取反就可以去掉

    然后你注意一下异或FWT的本质

    [c_i=sum_j(-1)^{bitcount(i&j)}a_j ]

    可以惊奇的发现

    (c_i+sum a=2b_i)

    其实就是系数对应起来是01与-1,1,然后加上整体除个2就是了

    于是可以直接求出(c),然后对(c)做一个FWT逆变换就可以了


    Code:

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    template <class T>
    void read(T &x)
    {
    	x=0;char c=getchar();
    	while(!isdigit(c)) c=getchar();
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    }
    const int N=(1<<20)+10;
    int n;
    ll b[N];
    void FWT(int len)
    {
    	for(int le=1;le<len;le<<=1)
    		for(int p=0;p<len;p+=le<<1)
    			for(int i=p;i<p+le;i++)
    			{
    				ll x=b[i],y=b[i+le];
    				b[i]=(x+y)/2;
    				b[i+le]=(x-y)/2;
    			}
    }
    int main()
    {
        freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
       	read(n);
       	for(int i=0;i<n;i++) read(b[i]);
       	for(int i=0;i<n;i++)
       		if(i<n-1-i)
    			std::swap(b[i],b[n-1-i]);
    	for(int i=n-1;~i;i--)
    		b[i]=(b[i]<<1)-b[0];
    	FWT(n);
    	for(int i=0;i<n;i++) printf("%lld ",b[i]);
    	return 0;
    }
    

    C

    交互题。要求还原一颗(n(le 100000))个点的树,(query(x,y,z))表示询问离这三个点距离和最小的点,询问次数不超过(1000000)次,树的形态随机。


    这里树的形态随机表示prufer序列随机,特点是每个点期望度数是(O(1))

    那么可以随机点分治

    具体操作是随机两个端点,然后询问这个联通块所有点,把这条链抽出来,然后把这个联通块的点划分到链上的点去,递归处理子问题,可以证明这是一个小常数的(O(nlog n))


    Code:

    #include "c.h"
    #include <algorithm>
    #include <vector>
    #include <ctime>
    std::vector<int> yuu[100010];
    int X;
    bool cmp(int x,int y){return query(X,x,y)==x;}
    void dfs(std::vector<int> v)
    {
    	if(v.size()==1) return;
    	if(v.size()==2)
    	{
    		check(v[0],v[1]);
    		return;
    	}
    	for(int i=0;i<v.size();i++)
    		yuu[v[i]].clear();
    	std::random_shuffle(v.begin(),v.end());
    	int x=v[0],y=v[1];
    	std::vector<int> yuy;
    	for(int i=0;i<v.size();i++)
    	{
    		int z=v[i],now=query(x,y,z);
    		yuu[now].push_back(z);
    		if(now==z) yuy.push_back(now);
    	}
    	X=x;
    	std::sort(yuy.begin(),yuy.end(),cmp);
    	for(int i=1;i<yuy.size();i++)
    		check(yuy[i-1],yuy[i]);
    	for(int i=0;i<yuy.size();i++)
    		dfs(yuu[yuy[i]]);
    }
    void solve(int n)
    {
    	srand(time(0));
    	for(int i=1;i<=n;i++) yuu[1].push_back(i);
    	dfs(yuu[1]);
    }
    
  • 相关阅读:
    关于数据集的划分--训练集、验证集和测试集
    关于过拟合的问题总结
    paddle 09-13
    关于NLP多分类任务评价指标的总结
    数组题解
    多进程-协程
    多任务-进程
    多任务-线程
    网络-tcp
    网络-udp
  • 原文地址:https://www.cnblogs.com/butterflydew/p/10623713.html
Copyright © 2020-2023  润新知