码迷,mamicode.com
首页 > 其他好文 > 详细

3.29 考试

时间:2019-03-29 21:01:55      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:注意   begin   wap   操作   head   去掉   这一   表示   type   

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,k\le 20,k\in \Z)\)的正整数序列\(a_i\),且有
\[ b_i=\sum_{0\le j<n}((bitcount((i \ or \ j)xor \ i)+1)\bmod 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(n\log 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]);
}

3.29 考试

标签:注意   begin   wap   操作   head   去掉   这一   表示   type   

原文地址:https://www.cnblogs.com/butterflydew/p/10623713.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!