长乐集训 Day1

A.divide

题意

给定正整数 $n,m$,构造方案使得 $m$ 分为 $n$ 个正整数的和,使得这些正整数的最小值大于它们的异或和。

Solution

如果 $n=1$ 显然无解。

如果 $n=2$:

  • $m$ 为偶数,直接构造 $\frac{m}{2},\frac{m}{2}$ 就好了。

  • $m$ 为奇数,当 $m\ne 2^k-1$ 时候构造 $\lfloor\frac{m}{2}\rfloor,\lfloor\frac{m}{2}\rfloor+1$ ,否则无解。

然后不会做了。以为 $3\le n \le 10^5$ 部分是神仙 dp 题,然后题解告诉我是傻逼大分讨,找规律写完就好了,让最小值为 $1$ 或者 $2$ 然后构造异或值为 $0$ 或者 $1$。

Code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int res=0,flag=1;
    char ch=getchar();
    while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
    while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
    return res*flag;
}
void solve(int n,int m)
{
    if(n==1)
    {
        printf("-1\n");
        return ;
    }
    else if(n==2)
    {
        if(m%2==0)
            printf("%d %d\n",m/2,m/2);
        else 
        {
            int a=m/2,b=m-m/2;
            if((a^b)>=std::min(a,b))
                printf("-1\n");
            else
                printf("%d %d\n",a,b);
        }
        return ;
    }
    else if(n==3)
    {
        int high=(1<<(int)log2(m));
        if(m%2==0)
        {
            if(m==2||m==4||m==8)
                printf("-1\n");
            else if(high==m)
                printf("%d %d %d\n",3*(m/16),6*(m/16),7*(m/16));
            else
                printf("%d %d %d\n",m/2,(m-high)/2,high/2);
        } 
        else 
        {
            int a=m/2,b=(m-high)/2,c=high/2+1;
            if(m==high+1||m==high+3)
            {
                if(m<=19)
                    printf("-1\n");
                else if(m==high+1)
                    printf("%d %d %d\n",3*(int)(m/16)+1,6*(int)(m/16),7*(int)(m/16));
                else if(m==high+3)
                    printf("%d %d %d\n",3*(int)(m/16)+1,6*(int)(m/16)+1,7*(int)(m/16)+1);
            }
            else if((a^b^c)>=std::min(a,std::min(b,c)))
                printf("-1\n");
            else
                printf("%d %d %d\n",a,b,c);
            //  printf("%d %d %d\n",(int)(m/2),(int)(m-high)/2,(int)(high/2)+1);
        }
        return ;
    }
    else if(n==4)
    {
        if(m%2==0)
            printf("%d %d 1 1\n",int(m/2)-1,int(m/2)-1);
        else 
        {
            if(m<=7)
                printf("-1\n");
            else
                printf("%d %d 2 3\n",(m-5)/2,(m-5)/2);
        }
        return ;
    }
    else if(n==5)
    {
        if(m%2==0)
        {
            if(m==6)
                printf("-1\n");
            else
                printf("%d %d 1 2 3\n",(int)(m/2)-3,(int)(m/2)-3);
        }
        else 
        {
            if(m<=15)
                printf("-1\n");
            else
                printf("%d %d 2 5 6\n",(int)(m-13)/2,(int)(m-13)/2);
        }
        return ;
    }
    else if(n%2==0)
    {
        if(m%2==0)
        {
            for(int i=1;i<=n-4;i++)
                printf("1 ");
            solve(4,m-n+4);
        }
        else
        {
            if(m<=2*n-1)
                printf("-1\n");
            else
            {
                for(int i=1;i<=n-4;i++)
                    printf("2 ");
                solve(4,m-n*2+8);
            }
        }
        return ;
    }
    else
    {
        if(m%2==0)
        {
            if(m==n+1)
                printf("-1\n");
            else
            {
                for(int i=1;i<=n-5;i++)
                    printf("1 ");
                solve(5,m-n+5);
            }
        }
        else
        {
            if(m<=2*n+5)
                printf("-1\n");
            else
            {
                for(int i=1;i<=n-5;i++)
                    printf("2 ");
                solve(5,m-n*2+10);
            }
        }
        return ;
    }
    return ;
}
int main(int argc,const char *argv[])
{
    freopen("divide.in","r",stdin);
    freopen("divide.out","w",stdout);
    int T=read();
    while(T--)
    {
        int n=read(),m=read();
        solve(n,m);
    }
    return 0;
}

B.color

题意

给定一棵以 $1$ 为根的树,对其染色,要求满足若干 A,B 限制。

  • A 限制:以 $pos$ 为根节点的子树内至少要有 $num$ 个节点被染色

  • B 限制:以 $pos$ 为根节点的子树外至少要有 $num$ 个节点被染色

Solution

先考虑 $B=0$ 的情况,直接贪心在树上合并,做一个类似 dp 的东西,处理出每个节点的子树中至少要染色的节点数。

然后发现答案具有单调性,大于最优答案的所有答案都可以满足条件。考虑二分,对于当前 $mid$ 去对整棵树 dfs 一次。设当前考虑到的点为 $pos$,则对于一个 B 限制,$pos$ 的子树外要染 $num$ 个节点等价于 $pos$ 的子树内至多染 $mid-num$ 个节点。这样只需要 check 每个节点是否满足上下界限制即可。

Code

#include<bits/stdc++.h>
inline int read()
{
    int res=0,flag=1;
    char ch=getchar();
    while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
    while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
    return res*flag;
}
struct edge
{
    int to,nxt;
};
bool flag;
int n,tot,mid,ans;
bool color[100010];
int head[100010],num[100010];
int size[100010],used[100010],rst[100010];
struct edge ed[200010];
void add_edge(int fr,int to)
{
    ed[++tot]=(edge){to,head[fr]};
    head[fr]=tot;
    return ;
}
void init(int fr,int fa)
{
    size[fr]=1;
    for(int i=head[fr];i!=0;i=ed[i].nxt)
    {
        int to=ed[i].to;
        if(to==fa)
            continue;
        init(to,fr);
        size[fr]+=size[to];
        used[fr]+=used[to];
    }
    if(num[fr]==0)
        return ;
    if(size[fr]<num[fr])
    {
        printf("-1");
        exit(0);
    }
    used[fr]=std::max(used[fr],num[fr]);
    return ;
}
void dfs(int fr,int fa)
{
    num[fr]=mid-rst[fr];
    int tmp=1;
    for(int i=head[fr];i!=0;i=ed[i].nxt)
    {
        int to=ed[i].to;
        if(to==fa)
            continue;
        dfs(to,fr);
        tmp+=num[to];
    }
    num[fr]=std::min(tmp,num[fr]);
    if(num[fr]<used[fr])
        flag=false;
    return ;
}
int main(int argc,const char *argv[])
{
    freopen("color.in","r",stdin);
    freopen("color.out","w",stdout);
    n=read();
    for(int i=1;i<n;i++)
    {
        int fr=read(),to=read();
        add_edge(fr,to);
        add_edge(to,fr);
    }
    int q=read();
    for(int i=1;i<=q;i++)
    {
        int pos=read(),tmp=read();
        num[pos]=std::max(num[pos],tmp);
    }
    init(1,0);
    q=read();
    for(int i=1;i<=q;i++)
    {
        int pos=read(),tmp=read();
        if(tmp>n-size[pos])
        {
            printf("-1");
            return 0;
        }
        rst[pos]=std::max(rst[pos],tmp);
    }
    int left=0,right=n;
    while(left<right)
    {
        mid=(left+right)>>1;
        flag=true;
        dfs(1,0);
        if(flag==true)
            right=mid;
        else 
            left=mid+1;
    }
    printf("%d",left);
    return 0;
}

C.query

题意

给定一个长度为 $n$ 的排列 $p$,$m$ 次给定 $l,r$ 求:$$ \sum^{r}_{i=l}\sum^{r}_{j=i+1}\sum^{r}_{k=j+1} p_k\times[\gcd(p_i,p_j)=p_k] $$

Solution

离线,然后乱搞。固定右端点 $r$,维护每个 $l$ 的答案。

Code

D.compete

题意

给定一个 01 串,两个人轮流取数,A 只能取 $0$,B 只能取 $1$,问两人都采取最优策略下,是否有人必胜或全部取完后平局。

Solution

一眼秒了,直接双指针模拟。设当前能取的数为 $opt$,左右端点分别为 $l,r$,则有以下情况:

  • $l,r$ 中只有一个权值为 $opt$,则必取该点;

  • $l,r$ 权值都不为 $opt$ 当前操作者败;

  • $l,r$ 权值都为 $opt$;

    • $l+1,r-1$ 中有一个权值为 $opt$,则取连续两个的那一段,此时操作者必胜;

    • $l+1,r-1$ 权值都为 $!opt$ 且 $l+2,r-2$ 权值都为 $!opt$ 则无论取哪个都必败;

    • $l+1,r-1$ 权值都为 $!opt$ 且 $l+2,r-2$ 权值中有一个为 $opt$ 则显然取往后第二个为 $opt$ 的那侧更优。

模拟即可。

Code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int res=0,flag=1;
    char ch=getchar();
    while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
    while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
    return res*flag;
}
int val[100010];
string solve()
{
    int n=read();
    for(int i=1;i<=n;i++)
        scanf("%1d",&val[i]);
    int fr=1,to=n,opt=0;
    while(to>=fr)
    {
        if(val[fr]==opt&&val[to]==opt)
        {
            if(val[fr+1]==opt) fr++;
            else if(val[to-1]==opt) to--;
            else if(val[fr+2]==opt) fr++;
            else if(val[to-2]==opt) to--;
            else fr++;
        }
        else if(val[fr]==opt) fr++;
        else if(val[to]==opt) to--;
        else 
            return (opt==0)?"ymw\n":"sch\n";
        opt^=1;
    }
    return "neck and neck\n";
}
int main(int argc,const char *argv[])
{
    freopen("compete.in","r",stdin);
    freopen("compete.out","w",stdout);
    int T=read();
    while(T--)
        cout<<solve();
    return 0;
}

result

看完题觉得 D 题很水直接 10min 写掉了,然后去写了个 A $n\le2$ 的 20pts,然后发现 C 题 $n,m\le 100$ 的部分分十分好写就写掉了。然后去想 C 题,做完 $B=0$ 的部分之后发现答案可以二分,写掉了,此时大概在 3h30min 左右。预计得分 20+100+30+100=250pts。后来一个小时一分没写。

挂分了。B 题上界没判好然后 $B=0$ 的 30pts 都挂掉了。蚌。

posted @ 2023-11-06 22:12  Che_001  阅读(23)  评论(0)    收藏  举报  来源