寒假学习计划

比赛地址

题目地址

题解

[E] 1-Trees and Queries

题意

给出一棵树以及q个询问,每个询问的形式是a,b,x,y,k.即判断在树上添加了一条xy的边之后,是否存在一条从ab的,长度为k的路径(可以重复走过任何一条边任意次)

题解

Dis(a,b)=l,问题不难转化为判断lk是否奇偶.假如同奇偶,就可以通过反复走某一条边使得路径的长度变成k.假如不同奇偶,添加了xy的边后,会形成一个环,只需再判断这个环的长度是不是奇数,也就是能不能通过走一次这个环改变l的奇偶性.

如果l>k,只需判断一下能否走x->y这条近路减少路径长度即可.

代码

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define IL inline
#define reg register
#define LL long long
#define N 100010
#define INF 0x3f3f3f3f
using namespace std;

int n,i,x,y,a,b,q,k,l,L,l2,L2,lsum=1;
int head[N],hson[N],top[N],size[N],msize[N],d[N],fa[N];

struct Edge{
    int t,next;
}e[N*8];

IL int Abs(int x){return (x<0)?-x:x;}
IL void Swap(int &a,int &b){a^=b^=a^=b;}
IL int Min(int a,int b){return (a<b)?a:b;}
IL int Max(int a,int b){return (a>b)?a:b;}

IL int read(){
    int p=0,f=1;    char    c=getchar();
    while (c<48||c>57)  {if (c=='-')    f=-1;   c=getchar();}
    while (c>=48&&c<=57)    p=(p<<1)+(p<<3)+c-48,c=getchar();
    return p*f;
}

IL void Add(int s,int t){
    e[lsum].t=t;    e[lsum].next=head[s];   head[s]=lsum++;
}

IL void Dfs(int x,int f){
    int i=0;
    size[x]++;
    for (i=head[x];i;i=e[i].next){
        if (e[i].t == f)    continue;
        d[e[i].t]=d[x]+1;   Dfs(e[i].t,x);
        fa[e[i].t]=x;
        if (size[e[i].t]>size[hson[x]])
            hson[x]=e[i].t;
        size[x]+=size[e[i].t];
    }
}

IL void Cut(int x){
    int i=0;
    top[hson[x]]=top[x];
    if (hson[x])    Cut(hson[x]);
    for (i=head[x];i;i=e[i].next){
        if (e[i].t==hson[x] || e[i].t==fa[x])   continue;
        top[e[i].t]=e[i].t; Cut(e[i].t);
    }
}

IL int LCA(int x,int y){
    while (top[x]^top[y])
        (d[top[x]]>d[top[y]])?x=fa[top[x]]:y=fa[top[y]];
    return (d[x]<d[y])?x:y;
}

IL int Dis(int x,int y){
    reg int p=LCA(x,y);
    return d[x]+d[y]-2*d[p];
}

int main(){
    #ifdef __Marvolo
    freopen("zht.in","r",stdin);
    freopen("zht.out","w",stdout);
    #endif
    n=read();
    for (i=1;i<n;i++){
        x=read();   y=read();
        Add(x,y);   Add(y,x);
    }
    Dfs(1,0);   top[1]=1;   Cut(1);
    q=read();
    while (q--){
        x=read();   y=read();   a=read();   b=read();
        k=read();
        l=Dis(a,b); L=Dis(x,y);
        if (k<l){
            l2=Min(Dis(a,x)+Dis(y,b)+1,Dis(a,y)+Dis(x,b)+1);
            if (l2>k)   {
                puts("NO");
                continue;
            }
            if (!((k-l2)%2))    {puts("YES");   continue;}
            puts("NO");
            continue;
        }
        if (!((k-l)%2)) {puts("YES");   continue;}
        l2=Dis(a,x)+Dis(y,b)+1; L2=Dis(a,y)+Dis(x,b)+1;
        if (l2<=k){
            if (!((k-l2)%2))    {puts("YES");   continue;}
            if (!(L%2) && l2+L+1<=k)    {puts("YES");   continue;}
        }
        if (L2<=k){
            if (!((k-L2)%2))    {puts("YES");   continue;}
            if (!(L%2) && L2+L+1<=k)    {puts("YES");   continue;}
        }
        puts("NO");
    }
    return 0;
}

[F] Animal Observation (hard version)

题意

太复杂了,见题目链接

题解

f[i][j]表示第i天的相机拍摄[j,j+k-1]区域所能拍到动物的最大数量.

有如下转移方程:

f[i][j]=Max(f[i][j],f[i-1][g]+Cost([j,j+k-1]))

因为Cost([j,j+k-1])是会受g的选取影响的,所以在转移的时候要消除这个影响.

从左向右枚举,一开始的区间是[1,k],此时,上一天的区间左端点在[1,k]之间的会和这次有重叠,把重叠的部分减去,然后找f^{'}[i-1][g]的最大值即可.枚举的区间向右移动的时候,左端点处收到影响的区间再加上对应的值,包含即将成为右端点的区间要减去对应的值.最大值的查询和修改通过线段树维护.总的时间复杂度为O(nmlogm).具体细节见代码.

题解里还有一个用单调队列的O(nm)解法.

代码

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define IL inline
#define reg register
#define LL long long
#define N 200010
#define INF 0x3f3f3f3f
using namespace std;

int n,m,k,i,j,g,h,l,r;
LL Ans,sum;
LL a[55][N],s[55][N],f[55][N];

struct Tree{
    int l,r;
    LL d,lz;
}t[N*4];

inline LL Min(LL a,LL b){return (a<b)?a:b;}
inline LL Max(LL a,LL b){return (a>b)?a:b;}

inline int read(){
    int p=0;    char    c=getchar();
    while (c<48||c>57)  c=getchar();
    while (c>=48&&c<=57)    p=(p<<1)+(p<<3)+c-48,c=getchar();
    return p;
}

IL void Maketree(int x,int low,int high){
    int mid=(low+high)>>1;
    t[x].l=low; t[x].r=high;
    if (low==high)  return;
    Maketree(l(x),low,mid); Maketree(r(x),mid+1,high);
}

IL void Push(int x){
    LL p=t[x].lz;
    t[l(x)].d+=p;   t[r(x)].d+=p;
    t[l(x)].lz+=p;  t[r(x)].lz+=p;
    t[x].lz=0;
}

IL void Add(int x,int low,int high,LL val){
    int mid=(t[x].l+t[x].r)>>1;
    if (low>m || low>high)  return;
    if (t[x].l==low && t[x].r==high)    {t[x].d+=val;   t[x].lz+=val;   return;}
    if (t[x].lz)    Push(x);
    if (high<=mid)  Add(l(x),low,high,val);
    else if (low>mid)   Add(r(x),low,high,val);
    else Add(l(x),low,mid,val),Add(r(x),mid+1,high,val);
    t[x].d=Max(t[l(x)].d,t[r(x)].d);
}

IL void Change(int x,int low,LL val){
    int mid=(t[x].l+t[x].r)>>1;
    if (t[x].l==t[x].r) {t[x].d=val;    return;}
    if (t[x].lz)    Push(x);
    (low<=mid)?Change(l(x),low,val):Change(r(x),low,val);
    t[x].d=Max(t[l(x)].d,t[r(x)].d);
}

IL LL Query(int x,int low,int high){
    int mid=(t[x].l+t[x].r)>>1;
    if (low==t[x].l && high==t[x].r)    return t[x].d;
    if (t[x].lz)    Push(x);
    if (high<=mid)  return Query(l(x),low,high);
    else if (low>mid)   return Query(r(x),mid+1,high);
    else return Max(Query(l(x),low,mid),Query(r(x),mid+1,high));
}

IL void Ready(){
    reg int i=0,j=0;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
            a[i][j]=read();
    memset(s,0,sizeof(s));  memset(f,0,sizeof(f));
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
            s[i][j]=s[i][j-1]+a[i][j];
    Maketree(1,1,m);
    for (i=1;i<=m-k+1;i++){
        f[1][i]=s[1][i+k-1]-s[1][i-1]+s[2][i+k-1]-s[2][i-1];
        Add(1,i,i,f[1][i]);
    }
}

int main(){
    #ifdef __Marvolo
    freopen("zht.in","r",stdin);
    freopen("zht.out","w",stdout);
    #endif
    n=read();   m=read();   k=read();
    Ready();
    for (i=2;i<=n;i++){
        for (j=1;j<=k;j++)
            Add(1,j,j,-(s[i][k]-s[i][j-1]));
        for (j=1;j<=m-k+1;j++){
            sum=t[1].d;
            f[i][j]=sum+s[i][j+k-1]-s[i][j-1]+s[i+1][j+k-1]-s[i+1][j-1];
            l=Max(1,j-k+1); Add(1,l,j,a[i][j]);
            r=Min(m,j+k);   Add(1,j+1,r,-a[i][j+k]);
        }
        for (j=1;j<=m;j++)  Change(1,j,f[i][j]);
    }
    for (i=1;i<=m;i++)  Ans=Max(Ans,f[n][i]);
    cout<<Ans<<endl;
    return 0;
}
说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...