Educational Codeforces Round 48 D E F 题解

D

一道不错的构造题。想到正解后可以很快的AC,想不到也可以有其它方法。我属于后者,比赛时我大概的做法是先把每一行填上它的异或值,然后再通过一行同时异或上相同的数来调整列的异或值。正解则十分简洁,只有第一行与第一列填数就行了,其它可以全为0,证明也十分简单,假如存在一组解,那么假设右下角的数为x,就可以把右下角的四个数同时异或上x,把右下角的数变为0,以此类推,那么最后只会剩下第一行与第一列的数。

我的代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int inf=2147483647;
const int Maxn=110;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,a[Maxn],b[Maxn],c[Maxn][Maxn];
int main()
{
    memset(c,0,sizeof(c));
    bool O=false;
    n=read(),m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=m;i++)b[i]=read();
    if(n>m)
    {
        O=true;
        swap(n,m);
        for(int i=1;i<=m;i++)swap(a[i],b[i]);
    }
    for(int i=1;i<=n;i++)c[i][i]=a[i];
    for(int j=1;j<=n;j++)
    {
        if(j!=n)c[j+1][j]^=(b[j]^a[j]);
        else c[1][j]^=(b[j]^a[j]);
        if(n==m)
        {
            if(j!=n)c[j+1][n]^=(b[j]^a[j]);
            else c[1][n]^=(b[j]^a[j]);
        }
        else
        {
            if(j!=n)c[j+1][n+1]^=(b[j]^a[j]);
            else c[1][n+1]^=(b[j]^a[j]);
        }
    }
    for(int j=n+1;j<m;j++)
    {
        int now=0;
        for(int i=1;i<=n;i++)now^=c[i][j];
        c[1][j]^=(now^b[j]);
        c[1][j+1]^=(now^b[j]);
    }
    bool flag=true;
    for(int i=1;i<=n;i++)
    {
        int now=0;
        for(int j=1;j<=m;j++)now^=c[i][j];
        if(now!=a[i]){flag=false;break;}
    }
    if(!flag)return puts("NO"),0;
    for(int j=1;j<=m;j++)
    {
        int now=0;
        for(int i=1;i<=n;i++)now^=c[i][j];
        if(now!=b[j]){flag=false;break;}
    }
    if(!flag)return puts("NO"),0;
    puts("YES");
    if(!O)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            printf("%d ",c[i][j]);
            puts("");
        }
    }
    else
    {
        for(int j=1;j<=m;j++)
        {
            for(int i=1;i<=n;i++)
            printf("%d ",c[i][j]);
            puts("");
        }
    }
}

E:

显然,每个询问点到线段两端点连线在线上的一定是连续的一段,那么我们可以二分出左端点与右端点,然后再用相似三角形的知识求解即可。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=200010;
const double eps=1e-6;
double sy,a,b,sum[Maxn];
struct Node{double l,r;}A[Maxn];
bool cmp(Node a,Node b){return a.l<b.l;}
int n,q;
int main()
{
    scanf("%lf%lf%lf",&sy,&a,&b);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lf%lf",&A[i].l,&A[i].r);
    sort(A+1,A+1+n,cmp);
    sum[0]=0;
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+A[i].r-A[i].l;
    scanf("%d",&q);
    while(q--)
    {
        double x,y;
        scanf("%lf%lf",&x,&y);
        int L1=1,R1=n;
        while(L1<=R1)
        {
            int Mid=L1+R1>>1;
            if(abs(x-A[Mid].l)<=eps)
            {
                if(x<=b)L1=Mid+1;
                else R1=Mid-1;
            }
            else
            {
                double K=y/(x-A[Mid].l),B=y-K*x;
                double X=(sy-B)/K;
                if(X<=b)L1=Mid+1;
                else R1=Mid-1;
            }
        }
        L1--;
        int L2=1,R2=n;
        while(L2<=R2)
        {
            int Mid=L2+R2>>1;
            if(abs(x-A[Mid].r)<=eps)
            {
                if(x>=a)R2=Mid-1;
                else L2=Mid+1;
            }
            else
            {
                double K=y/(x-A[Mid].r),B=y-K*x;
                double X=(sy-B)/K;
                if(X>=a)R2=Mid-1;
                else L2=Mid+1;  
            }
        }
        R2++;
        double t1=0.0,t2=0.0;
        if(abs(x-A[L1].r)>eps)
        {
            double K=y/(x-A[L1].r),B=y-K*x;
            double X=(sy-B)/K;
            if(X-b>eps)t1=X-b;
        }
        else if(x>b)t1=x-b;
        if(abs(x-A[R2].l)>eps)
        {
            double K=y/(x-A[R2].l),B=y-K*x;
            double X=(sy-B)/K;
            if(a-X>eps)t2=a-X;
        }
        else if(a>x)t2=a-x;
        double ans=(sum[L1]-sum[R2-1])*(y-sy)/y-t1-t2;
        if(ans<eps)puts("0.0000000000");
        else printf("%.10lf\n",ans);
    }
}

F:

这题不错。首先有个结论:在1到n这条链上的点,儿子中除了链上的点还有2个或以上的,答案一定全是原来树上的距离,这个画个图就很好理解:
这里写图片描述
有2个或以上其它的儿子就分为上图的两种情况,这时只需要把边像红色的边那样建就可以了,答案是最优的。假如没有这样的情况,那么这个树一定是一条链加上几个点,根据其特殊性,找边在哪两个点之间连就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=300010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,fa[Maxn],son[Maxn],to[Maxn];LL dis[Maxn],Mx[Maxn];
struct Edge{int y,next;LL d;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y,LL d)
{
    int t=++len;
    e[t].y=y;e[t].d=d;e[t].next=last[x];last[x]=t;
}
bool flag=false;
bool dfs(int x,int f)
{
    fa[x]=f;son[x]=0;
    bool re=false;
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(y==f)continue;
        dis[y]=dis[x]+e[i].d;
        bool tf=dfs(y,x);
        if(!tf)son[x]+=son[y]+1;
        re|=tf;
    }
    if(x==n&&son[x]>1)flag=true;
    else if(son[x]>1)flag=true;
    if(x==n)re|=true;
    return re;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();LL d=read();
        ins(x,y,d),ins(y,x,d);
    }
    dis[1]=0;
    dfs(1,0);
    if(flag)
    {
        while(m--)printf("%lld\n",dis[n]);
        return 0;   
    }
    int x=n;
    while(x!=1)
    {
        to[fa[x]]=x;
        x=fa[x];
    }
    LL ans=-(1LL<<60);
    Mx[1]=0;
    if(son[1])
    {
        for(int i=last[1];i;i=e[i].next)
        {
            int y=e[i].y;
            if(to[1]==y)continue;
            Mx[1]=e[i].d;
        }
    }
    x=to[1];int l1=1,l2=0;
    while(1)
    {
        LL D=0;
        if(x==n)
        {
            if(!son[x]&&!son[l1])ans=max(ans,Mx[l2]-(dis[n]-dis[l2]));
            else
            {
                for(int i=last[x];i;i=e[i].next)
                {
                    int y=e[i].y;
                    if(y==fa[x])continue;
                    D=e[i].d;
                }
                ans=max(ans,D+Mx[l1]-(dis[n]-dis[l1]));
            }
            break;
        }
        if(son[x])
        {
            for(int i=last[x];i;i=e[i].next)
            {
                int y=e[i].y;
                if(y==to[x]||y==fa[x])continue;
                D=e[i].d;
            }
        }
        if(!son[x]&&!son[l1])
        {
            if(l2)ans=max(ans,Mx[l2]-(dis[x]-dis[l2]));
        }
        else ans=max(ans,Mx[l1]-(dis[x]-dis[l1])+D);
        Mx[x]=max(Mx[l1]-(dis[x]-dis[l1]),D);
        l2=l1,l1=x;
        x=to[x];
    }
    while(m--)
    {
        int d=read();
        printf("%lld\n",min(dis[n],dis[n]+d+ans));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值