• COCI 2018/2019 CONTEST #2 Solution


    Problem1 Preokret 

    第一题一定不是什么难题。

    第一个问题在读入的时候判断当前时间是不是在1440及以前就行

    第二个问题考虑离线处理,由于每个时刻只能最多发生1个事件那么就弄个桶记录每一个事件就行了。

    水过T1。

    Code:

    # include <bits/stdc++.h>
    using namespace std;
    const int N=2881;
    int a[N];
    int A,B,T,rec1,rec2;
    template <typename T>inline void read(T &x)
    {
        int w=0,X=0; char c=0;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
        x=w?-X:X;
    }
    int main()
    {
    //    freopen("Preokret.in","r",stdin);
    //    freopen("Preokret.out","w",stdout);
        memset(a,-1,sizeof(a));
        read(A); 
        for (int i=1;i<=A;i++) read(T),a[T]=0; 
        read(B);
        for (int i=1;i<=B;i++) read(T),a[T]=1;
        int Ans1=0,Ans2=0,flag=-1,s1=0,s2=0;
        for (int i=1;i<=2880;i++) {
            if (a[i]==-1) continue;
            if (a[i]==0) s1++; else s2++;
            if (i<=1440) Ans1++;
            if (flag==-1) { flag=s1<s2; continue;}
            if (s1>s2&&flag==1) flag=0,Ans2++;
            else if (s1<s2&&flag==0) flag=1,Ans2++;
        }
        cout<<Ans1<<'
    '<<Ans2<<'
    ';
        return 0;
    }

    Problem2 Kocka

    这个题对于100%的数据一定不是O(n2)的构造,而是数学的判断。

    我们可以简单考虑四种情况,我们记四个数组分别是left[],right[],up[],down[],

    分别考虑四个数组元素值是x的时候代表什么含义,

    以left[i]为例,left[i]=-1的时候表示第i行没有放东西,那么对应的right[i]必须也是-1否则构造不合法,而对于up[]和down[]无影响。

    left[i]=x(x≠-1时),那么意味着在(i,x+1)这个位置必须存在一个障碍物,那么从上面看下来可以看到的不能大于i-1吧,下面看上来不能超过n-i吧,右边看过来不能超过n-x-1吧。

    对于 right[]和up[]和down[]同理。

    这里需要注意在C++中up什么的可能是保留字,我Define了一下。

    Code

    # include <bits/stdc++.h>
    # define fp(i,s,t) for (int i=s;i<=t;i++)
    # define left Left    
    # define right Right
    # define up Up
    # define down Down
    using namespace std;
    const int N=1e5+10;
    int left[N],right[N],up[N],down[N],n;
    inline void read(int &x)
    {
        int w=0,X=0; char c=0;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
        x=w?-X:X;
    }
    bool work()
    {
        fp(i,1,n) {
            if (left[i]==-1) {
                if (right[i]==-1) continue;
                return 0;
            }
            if (up[left[i]+1]==-1||up[left[i]+1]>i-1) return 0;
            if (down[left[i]+1]==-1||down[left[i]+1]>n-i) return 0;
            if (right[i]==-1||right[i]>n-left[i]-1) return 0;
        }
        fp(i,1,n) {
            if (up[i]==-1) {
                if (up[i]==-1) continue;
                return 0;
            }
            if (left[up[i]+1]==-1||left[up[i]+1]>i-1) return 0;
            if (right[up[i]+1]==-1||right[up[i]+1]>n-i) return 0;
            if (down[i]==-1||down[i]>n-up[i]-1) return 0;
        }
        fp(i,1,n) {
            if (right[i]==-1) {
                if (left[i]==-1) continue;
                return 0;
            }
            if (up[n-right[i]]==-1||up[n-right[i]]>i-1) return 0;
            if (down[n-right[i]]==-1||down[n-right[i]]>n-i) return 0;
            if (left[i]==-1||left[i]>n-right[i]-1) return 0;
        }
        fp(i,1,n) {
            if (down[i]==-1) {
                if (up[i]==-1) continue;
                return 0;
            }
            if (left[n-down[i]]==-1||left[n-down[i]]>i-1) return 0;
            if (right[n-down[i]]==-1||right[n-down[i]]>n-i) return 0;
            if (up[i]==-1||up[i]>n-down[i]-1) return 0;
        }
        return 1;
    }
    int main()
    {
    //    freopen("Kocka.in","r",stdin);
    //    freopen("Kocka.out","w",stdout);
        read(n);
        fp(i,1,n) read(left[i]);
        fp(i,1,n) read(right[i]);
        fp(i,1,n) read(up[i]);
        fp(i,1,n) read(down[i]);
        puts(work()?"DA":"NE");
        return 0;
    }

    Problem3 Deblo

    这个题是树上路径计数,初步搞搞O(n2log2 n)暴力还是搞得来的。

    但是书上路径统计想到了淀粉质点分治暴力统计路径。

    这里需要注意点分治的一般步骤:

    每次在子树里面选定一个重心,考虑一条过重心的路径,统计这条边上的异或值。

    这里用到异或的一个性质x xor y xor y=x,考虑以子树的根(不一定是重心)dfs下去找到根的xor前缀和

    统计每个点的xor,那么就把01塞到桶里面就行了。

    注意这个时候由于每一条路径一定过重心所以先把重心加入,其他的前缀和先xor一个重心,这样可以把重心的影响减去。

    尤其注意先找这个点和其他点之间xor值然后再更新桶里面的值。

    重心打标及时啊!一塞入就打标。

    一开始先找重心可以快一点。。。

     具体的可以看我的点分治blog例题1:https://www.cnblogs.com/ljc20020730/p/10347198.html

    # include <bits/stdc++.h>
    # define LL long long
    # define fp(i,s,t) for (int i=s;i<=t;i++)
    using namespace std;
    const int N=1e5+10;
    const int MAXBIT=22;
    bool used[N];
    int size[N],val[N],cnt[2][MAXBIT],n;
    struct rec{int pre,to;}a[2*N];
    int tot,head[N];
    template <typename T>inline void read(T& t)
    {
        char c=getchar();t=0;
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')t=(t<<1)+(t<<3)+(c^48),c=getchar();
    }
    template <typename T,typename... Args> inline void read(T& t, Args&... args)
    {
        read(t);read(args...);
    }
    void write(LL x)
    {
        if (x>9) write(x/10);
        putchar('0'+x%10);
    }
    void writeln(LL x)
    {
        write(x);putchar('
    ');
    }
    void adde(int u,int v)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        head[u]=tot;
    }
    int Get_Root(int u,int fath,int S)
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||used[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=S/2) return Get_Root(pos,u,S);
        return u;
    }
    void Get_Dist(int u,int fath,int num)
    {
        num^=val[u]; size[u]=1;
        fp(i,0,MAXBIT-1)
            cnt[(num & (1<<i))>0][i]++;
            
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||used[v]) continue;
            Get_Dist(v,u,num);
            size[u]+=size[v];
        }
    }
    LL ans=0;
    void Get_Ans(int u,int fath,int num)
    {
        num^=val[u];
        fp(i,0,MAXBIT-1)
         ans+=(LL) (1<<i)*cnt[(num & (1<<i))==0][i];
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||used[v]) continue;
            Get_Ans(v,u,num);
        } 
    }
    void solve(int u,int fath)
    {
        int root=Get_Root(u,fath,size[u]);
        used[root]=1;
        memset(cnt,0,sizeof(cnt));
        fp(i,0,MAXBIT-1)
         cnt[(val[root] & (1<<i))>0][i]++;
        ans+=(LL)val[root];
        for (int i=head[root];i;i=a[i].pre) {
            int v=a[i].to;
            if (used[v]) continue;
            Get_Ans(v,0,0);
            Get_Dist(v,0,val[root]);
        }
        for (int i=head[root];i;i=a[i].pre) {
            int v=a[i].to;
            if (used[v]) continue;
            solve(v,u);
        } 
    }
    int main()
    {
        read(n); fp(i,1,n) read(val[i]);
        int u,v;
        fp(i,1,n-1) 
            read(u,v),adde(u,v),adde(v,u);
        int rt=Get_Root(1,0,n);
        Get_Dist(rt,0,0); solve(rt,0);
        writeln(ans);
        return 0;
    }

    Problem4 Maja

    数据加强的三个点其实就是把大多数标程卡掉的点。

    就是K不是偶数而是奇数的时候显然是不存在合法的一条路径的。

    这点原题中没有说需要判断。(但是神奇的是没有卡WTF)

    这道题显然K这么大不是搜索而是DP,

    搜索的复杂度O(4K)太大了吧!!! 而且每个点不止重复走1次。

    考虑一条性质:假设从原点(A,B)走到(i,j)恰好走了K/2步然后必然会选择走回去是最优的吧。

    这个显然。

    然后考虑全局的最优,答案一定是一个循环组成的,就是不停地在某一条路上来回走。

    这个也是显然的吧。

    那么有余数怎么办?可以证明贪心的选取中点四周的一个Ci,j最大的来回走完余数就是这个点最优的。

    这个可以反证? 简单yy一下如果不这样选取那么你还不如走到你最终的中点继续循环呢!

    然后就是一个DP+贪心的神仙题目了。

    先K/=2,只考虑去,回来其实近似乘以2就行。(其实并不是乘以2)

    设f[i][j][r]指的是从(A,B)走到(i,j)经过(i,j)有r次的最优解,那么显然是从四个方向走来的吧。

    转移是f[i][j][r]=Max{f[x][y][r-1]}+c[i][j](x,y指的是(i,j)四个相邻点)

    回去就是f[i][j][r]+Max{f[x][y][r-1]},那是Ci,j少记一次。

    然后剩余的步数(只记去,此时K已经除以2)就是K-r,余数走的步数就是

    (K-r)*(Max{c[x][y]}+c[i][j])

    然后每次到(i,j)r次计算一下答案。

    那个r一维可以滚存,r的取值范围是[0,Min(k,n*m)]

    Code:

    # include<bits/stdc++.h>
    # define fp(i,s,t) for (int i=s;i<=t;i++)
    # define inf (0x7f7f7f7f7fll)
    # define int long long
    using namespace std;
    const int N=105;
    int f[N][N][2],c[N][N];
    int n,m,A,B,K,ans;
    int Get_Max(int a,int b,int c,int d)
    {
        if (b>a) a=b;
        if (c>a) a=c;
        if (d>a) a=d;
        return a; 
    }
    template <typename T>inline void read(T& t)
    {
        char c=getchar();t=0;
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')t=(t<<1)+(t<<3)+(c^48),c=getchar();
    }
    template <typename T,typename... Args> inline void read(T& t, Args&... args)
    {
        read(t);read(args...);
    }
    void write(int x)
    {
        if (x>9) write(x/10);
        putchar('0'+x%10);
    }
    void writeln(int x)
    {
        write(x);putchar('
    ');
    }
    signed main()
    {
        read(n,m,A,B,K);
        if (K&1) { puts("0");exit(0);}
        K=K>>1;
        fp(i,0,n+1) fp(j,0,m+1)
         c[i][j]=f[i][j][0]=f[i][j][1]=-inf;
        fp(i,1,n) fp(j,1,m) read(c[i][j]);
        f[A][B][0]=0; ans=-inf;
        fp (r,1,min(K,n*m)) {
            int now=r&1;
            int pst=now^1;
            fp(i,1,n) fp(j,1,m) {
                int temp=Get_Max(f[i-1][j][pst],f[i+1][j][pst],f[i][j-1][pst],f[i][j+1][pst]);
                f[i][j][now]=max(temp+c[i][j],-inf);
                if (f[i][j][now]<0) continue;
                int Whole_Dist=f[i][j][now]+temp;
                Whole_Dist+=(K-r)*(c[i][j]+Get_Max(c[i-1][j],c[i+1][j],c[i][j-1],c[i][j+1]));
                ans=max(ans,Whole_Dist);
            }
        }
        writeln(ans);
        return 0;
     } 

    Problem5 Sunčanje

    一道暴力的神仙T5,一看到数据范围就不想暴力的。

    但是算算暴力的期望还是值得的。

    先按照矩形右下角的x坐标排序,然后枚举每一个长方形,i,看看前面的长方形j是不是包含在i里面的,

    被包含的必要条件是a[i].x-a[i].len<a[j].x对吧,不满足必要条件的那么前面也不满足必要条件,直接跳掉。

    然后花四个图看看判包含。

                 (a[i].y>a[j].y&&a[i].y<a[j].y+a[j].wid)
                ||(a[i].y+a[i].wid>a[j].y&&a[i].y+a[i].wid<a[j].y+a[j].wid)
                ||(a[j].y>a[i].y&&a[j].y<a[i].y+a[i].wid)
                ||(a[j].y+a[j].wid>a[i].y&&a[j].y+a[j].wid<a[i].y+a[i].wid)

    更新的时候注意看看谁在谁的下面(一定是初始标号小的放在下面!)

    然后瞎加点优化就可以用pascal过4000ms时限了(而C++不行)

    然后我就把第19个点的时限改成了6000ms,于是pascal和C++都过了

    如果C++代码在XOJ上过不了的话那么加一行

    # pragma G++ optimize(3)

    Code C++

    # pragma G++ optimize(3)
    # include <bits/stdc++.h> # define fp(i,s,t) for (int i=s;i<=t;i++) using namespace std; const int N=1e5+10; struct rec{ int x,y,len,wid,id; }a[N]; int n; bool ans[N]; inline int read() { int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X; } inline void qsort(int l,int r) { if (l==r) return; int t=rand()%(r-l)+l; swap(a[t],a[l]); rec v=a[l]; int i=l,j=r; while (i<j) { while (i<j&&a[j].x>v.x) j--; if (i<j) {a[i]=a[j];i++;} else break; while (i<j&&a[i].x<v.x) i++; if (i<j) {a[j]=a[i];j--;} else break; } a[i]=v; if (l<j) qsort(l,j-1); if (i<r) qsort(i+1,r); } signed main() { srand(time(NULL)*100007); n=read(); fp(i,1,n) { a[i].x=read();a[i].y=read();a[i].len=read();a[i].wid=read(); a[i].x+=a[i].len; a[i].id=i; } qsort(1,n); fp(i,2,n) { int j=i-1; while (j>=1&&(a[i].x-a[i].len<a[j].x)) { if ((a[i].y>a[j].y&&a[i].y<a[j].y+a[j].wid) ||(a[i].y+a[i].wid>a[j].y&&a[i].y+a[i].wid<a[j].y+a[j].wid) ||(a[j].y>a[i].y&&a[j].y<a[i].y+a[i].wid) ||(a[j].y+a[j].wid>a[i].y&&a[j].y+a[j].wid<a[i].y+a[i].wid)) { if (a[i].id<a[j].id) ans[a[i].id]=1; else ans[a[j].id]=1; } j--; } } fp(i,1,n) if (ans[i]) putchar('N'),putchar('E'),putchar(' '); else putchar('D'),putchar('A'),putchar(' '); return 0; }

    Code Pascal

    VAR 
        n,i,j:int64; x,y,len,wid,poza,raspuns:array[1..100001] of int64;
    function Poz(a1,b1 : int64) : int64;
    VAR aux,piv:int64;
    Begin
        piv:=x[a1];
        while (a1 < b1) do
        Begin
        if (x[a1] > x[b1]) then Begin 
            aux:=x[a1]; x[a1]:=x[b1]; x[b1]:=aux; 
            aux:=y[a1]; y[a1]:=y[b1]; y[b1]:=aux; 
            aux:=len[a1]; len[a1]:=len[b1]; len[b1]:=aux; 
            aux:=wid[a1]; wid[a1]:=wid[b1]; wid[b1]:=aux; 
            aux:=poza[a1]; poza[a1]:=poza[b1]; poza[b1]:=aux; end;
        if (piv = x[a1]) then b1:=b1 - 1 else a1:=a1 + 1;
        end;
        Poz:=a1;
    end;
    procedure Quick(a1,b1 : int64);
    VAR k:int64;
    Begin
        if (a1 < b1) then
            Begin
                k:=Poz(a1,b1);
                Quick(a1,k - 1);
                Quick(k + 1,b1);
        end;
    end;
    Begin
        Readln(n);
        for i:=1 to n do
        Begin
            Readln(x[i],y[i],len[i],wid[i]);
            x[i]:=x[i] + len[i];
            poza[i]:=i;
        end;
        Quick(1,n);
        raspuns[1]:=0;
        for i:=2 to n do
        Begin
            j:=i - 1;
            while (j > 0) and (x[i] - len[i] < x[j]) do
            Begin
                if     (((y[i] > y[j]) and (y[i] < y[j] + wid[j]))
                    or ((y[i] + wid[i] > y[j]) and (y[i] + wid[i] < y[j] + wid[j]))) 
                    or (((y[j] > y[i]) and (y[j] < y[i] + wid[i])) 
                    or ((y[j] + wid[j] > y[i]) and (y[j] + wid[j] < y[i] + wid[i]))) then
                Begin
                  if (poza[i] < poza[j]) then raspuns[poza[i]]:=1 else raspuns[poza[j]]:=1;
                end;
                j:=j - 1;
            end;
        end;
        for i:=1 to n do
            if (raspuns[i] = 1) then Writeln('NE') else Writeln('DA');
    END.
  • 相关阅读:
    http
    VUE-1
    AJAX
    html常用标签
    CSS网页布局
    概念整理3
    SEO
    概念整理2
    var
    概念整理
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/10350622.html
Copyright © 2020-2023  润新知