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<<'\n'<<Ans2<<'\n';
    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值然后再更新桶里面的值。

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

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

# 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('\n');
}
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('\n');
}
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++都过了

Code C++

# 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('\n');
     else putchar('D'),putchar('A'),putchar('\n');
    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.

猜你喜欢

转载自www.cnblogs.com/ljc20020730/p/10350622.html
今日推荐