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));
}
}