Codeforces Round #633 (Div. 1) / contest 1338


题目地址:https://codeforces.com/contest/1338



A Powered Addition

题意

思路

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
LL a[maxn];
int n;

int get(LL x,LL y)
{
    LL q=x-y;
    REP_(i,40,0) if(q&(1ll<<i)) return i;
    return -1;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        n=read();
        REP(i,1,n) a[i]=read();
        int ans=-1;
        REP(i,1,n-1) if(a[i]>a[i+1])
        {
            int x=get(a[i],a[i+1]);
            ans=max(ans,x);
            a[i+1]=a[i];
        }
        printf("%d\n",ans+1);
    }

    return 0;
}



B Edge Weight Assignment

题意:有一棵树,现在要给每条边分配一个边权,使得最后满足:所有叶子结点两两之间路径边权异或和为 0 。问所有分配方案中,边权不同的数目最多和最少是多少?(保证一定存在分配方案)

思路:如果选择一个叶子结点当做根结点,题目等价于所有其他叶子结点到根的边权异或和都为 0 。

先看最小的数目:如果某条根-叶路径的长度为偶数,那么全部分配一样的数就行了;如果是奇数,那么至少要 3 个不一样的数;所以这个答案事实上就是看叶结点深度有没有奇数。

然后是最大的数目:对于某个子树的根结点 u,它的度为1的儿子(也就是叶子)与 u 的连边的那个数一定都是一样的,而还有儿子的儿子就可以继续 dfs。但是这里还有个细节,如果叶子儿子的深度为 2,那就说明它还必须和 u 上面那条边相等。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
vector<int> G[maxn];
int n,du[maxn],root=1,ansmin=1,ansmax=0,d[maxn];
set<int> ds;

void dfs1(int u,int fa,int dd)
{
    d[u]=dd;
    if(G[u].size()==1 && fa)
    {
        ds.insert(dd);
        return;
    }
    REP(i,0,G[u].size()-1)
    {
        int v=G[u][i];
        if(v==fa) continue;
        dfs1(v,u,dd+1);
    }
}

void dfs2(int u,int fa)
{
    int flag=0;
    REP(i,0,G[u].size()-1)
    {
        int v=G[u][i];
        if(v==fa) continue;
        if(G[v].size()==1 && d[v]!=2) flag=1;
        else if(G[v].size()>1) ansmax++, dfs2(v,u);
    }
    if(flag) ansmax++;
}

int main()
{
    //freopen("input.txt","r",stdin);
    n=read();
    REP(i,1,n-1)
    {
        int u=read(),v=read();
        G[u].push_back(v);
        G[v].push_back(u);
        du[u]++; du[v]++;
    }
    while(du[root]>1) root++;
    dfs1(root,0,0);
    for(set<int>::iterator i=ds.begin();i!=ds.end();i++)
        if(*i&1) ansmin=3;
    dfs2(root,0);
    printf("%d %d",ansmin,ansmax);

    return 0;
}



C Perfect Triples

题意:在正整数集中,每次选出字典序最小的三个数 (a, b, c),使得 a b c = 0 a \bigoplus b\bigoplus c=0 ,并且 a、b、c 之前都没有选择过。把每次选的 (a, b, c) 加入队列末尾。然后有 T 次询问(1e5),每次询问给出一个正整数 n(1e16) ,要求给出队列中第 n 个数是多少?

思路:好吧其实我是打印出来找到了规律:(1)第一位的规律是 1, 4 5 6 7, 16 17 ... 31, … (2)第二位的规律是 2, 8 10 11 9, 32 34 35 33 ..., …,其中第二位后面满足 以四分割,从前往后分别是 x, x+2, x+3, x+1 这样的规律;(3)第三位其实就是前两位的异或。然后找到规律后就写出找第一位和第二位的函数就好了,其中第二位要用分治。

后来看了些其他博客,发现这是一个四进制的特点:1,2,3的两两异或其实是一个闭包(1^2=3, 1^3=2, 2^3=1)。然后也很容易找出规律(对于 a 的四进制的每一位,(a, b, c) 只有可能是 (0, 0, 0), (1, 2, 3), (2, 3, 1), (3, 1, 2)),那就只用求出 a 就可以了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

LL mi4(int k) {return 1ll<<(k*2);}
LL get1(LL n)
{
    LL x=0,k=0;
    while(x<n) x+=mi4(k++);
    k--; x-=mi4(k); x++; n-=x;
    return mi4(k)+n;
}

LL get2(LL xl,LL xr,LL ll,LL rr,LL n)
{
    if(xl+1>=xr) return ll;
    LL l[4],r[4],x=(rr-ll)/4;
    l[0]=ll; r[0]=ll+x;
    l[1]=ll+2*x; r[1]=ll+3*x;
    l[2]=ll+3*x; r[2]=ll+4*x;
    l[3]=ll+x; r[3]=ll+2*x;
    LL q=(n-xl)/x;
    return get2(xl+x*q,xl+x*(q+1),l[q],r[q],n);
}

LL get2(LL n)
{
    LL l=2,r=3,xl=1,xr=2,k=0;
    while(!(n>=xl && n<xr))
    {
        l*=4; r*=4; k++;
        xl=xr; xr+=mi4(k);
    }
    return get2(xl,xr,l,r,n);
}

LL get(LL n)
{
    if(n%3==1) return get1(n/3+1);
    else if(n%3==2) return get2(n/3+1);
    else return get1(n/3)^get2(n/3);
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        LL n;
        scanf("%lld",&n);
        printf("%lld\n",get(n));
    }

    return 0;
}



D Nested Rubber Bands

题意:给定一棵树,现在把每个结点变成一个闭合不自交曲线,并且满足:两个闭合曲线相交当且仅当原来的树上两个结点有连边。问在所有变化方式中,变之后存在的嵌套个数最多是多少个(嵌套是指一个闭合曲线包含另一个……)

思路:dfs+dp。对于每个结点定义 f(i, j) 表示结点 i 作为(j=1)嵌套中的一个或者不作为(j=0)嵌套中的一个时,其子树的最大嵌套数。每个子树的嵌套情况一定如下(左边那个中间其实还有很多,不过省略了):

其中如果子树根结点不作为嵌套,则上图中右边那个是根结点;如果子树根结点作为嵌套,则左边那个是根结点。

那么状态转移公式就有:
f ( u , 0 ) = m a x { f ( v , 0 ) , f ( v , 1 ) } + n u m ( o t h e r   s o n s ) f ( u , 1 ) = m a x { f ( v , 0 ) } + 1 \begin{aligned} f(u,0)&=max\{f(v,0),f(v,1)\}+num(other \ sons) \\ f(u,1)&=max\{f(v,0)\}+1 \end{aligned}
(这是通过画图得出来的:上面那个就是用 u 去交上一个最大的儿子,然后其它儿子都与 u 相交并且套在最大儿子的外面;下面那个就是用 u 去套最大儿子的根,因为要求 u 也要在嵌套内)

但是要注意到,上面的状态转移公式仅仅针对形成上面那张图,意思就是 f(i, j) 的意义下,某个子树的根一定在最外层(要么是上图左边,要么是右边),但要是考虑求最优答案时,还需要实时考虑如果当前的根在内层,去更新最优答案,这个通过画图就可以得到:(1)如果当前 u 不在嵌套,那么最优是选择最大的两个儿子相互嵌套,然后 u 去连接他们,同时 u 的其它儿子与 u 相连套在里面的大儿子上;(2)如果当前 u 在嵌套,那么最优是选择两个最大的根不嵌套的儿子,用 u 去嵌套地连接这两个儿子。

那么就 dp 计算 f,实时更新 ans 就行了。

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
vector<int> G[maxn];
int f[maxn][2],n,ans;

void dfs(int u,int fa)
{
    if(fa && G[u].size()==1) {f[u][1]=1; return;}
    for(int v:G[u]) if(v!=fa)
    {
        dfs(v,u);
        ans=max(ans,f[u][1]+max(f[v][0],f[v][1])+(int)(G[u].size()>1?G[u].size()-2:0));
        ans=max(ans,f[u][0]+f[v][0]+1);
        f[u][0]=max(f[u][0],f[v][0]);
        f[u][1]=max(f[u][1],max(f[v][0],f[v][1]));
    }
    swap(f[u][0],f[u][1]);
    f[u][0]+=(G[u].size()>1?G[u].size()-2:0);
    f[u][1]++;
}

int main()
{
    //freopen("input.txt","r",stdin);
    n=read();
    REP(i,1,n-1)
    {
        int u=read(),v=read();
        G[u].push_back(v); G[v].push_back(u);
    }
    dfs(1,0);
    cout<<ans;

    return 0;
}



E

题意

思路

代码


猜你喜欢

转载自blog.csdn.net/dragonylee/article/details/106672262