Educational Codeforces Round 132 (Rated for Div. 2)(补题中)

 加粗:赛时AC 

普通:赛后AC

A. Three Doors

水题,但我一开始没看懂题意,就随便猜了个题意。反正就是拿着钥匙开一扇门,门后有别的钥匙,问你能不能全打开。

int main()
{
    read(t);
    while(t--)
    {
        int x;
        read(x);
        memset(buc,0,sizeof(buc));
        for(int i=1;i<=3;i++) read(a[i]);
        buc[x]=1;
        buc[a[x]]=1;
        buc[a[a[x]]]=1;
        //buc[a[a[a[x]]]]=1;
        bool flag=1;
        for(int i=1;i<=3;i++) if(buc[i]==0) flag=0;
        if(!flag) printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}
View Code

B. Also Try Minecraft

依然没有完全看懂题意,但是模拟了一下样例,就是一个前缀和后缀和的问题,统计之间的差值。

int main()
{
    read(n);read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++)
    {
        if(a[i]>a[i+1]) p[i]=p[i-1]+(a[i]-a[i+1]);
        else p[i]=p[i-1];
    }
    for(int i=n;i>=1;i--)
    {
        if(a[i]>a[i-1]) rp[i]=rp[i+1]+(a[i]-a[i-1]);
        else rp[i]=rp[i+1];
    }
    for(int i=1;i<=m;i++)
    {
        ll x,y;
        read(x);read(y);
        if(x<y) printf("%lld\n",p[y-1]-p[x-1]);
        else printf("%lld\n",rp[y+1]-rp[x+1]);
    }
    return 0;
}
View Code

C. Recover an RBS

关于括号匹配,要遵循两个要点,一是一定要保证每个位置的左括号前缀和大于右括号的,第二点是最终左右括号个数相等。

这题我是这样考虑的。

题目给了一个很重要的条件,就是保证了必定存在至少一种情况符合匹配的模式串,那么如果答案唯一,应该是哪一种?

我们将左括号看做+1,右括号看做-1,那么两个要点就变为了任何地方的前缀和不为负并且最终前缀和为0。

显然,对于所有的?,我们可以根据左括号和等于右括号和,算出这当中有多少左右括号,然后将在前面等量的?换为需要的(,在后面的换为),这必定是一种可行的情况。

然后计算这个序列的前缀和,如果我们要变换我们填上的括号的方向,一定是将一组()变为)(,此时在这之中的所有前缀和都要-2,如果有负数,就不能变换。

显然我们应该变换位于中间的一组问号变得括号,此时削减的前缀和最少并且与其他的括号组合是被包含的关系。

int main()
{
    read(T);
    while(T--)
    {
        a=0;b=0;
        cin>>s;
        int len=s.size();
        for(int i=0;i<len;i++)
        {
            p[i]=0;
            if(s[i]=='(') a++;
            else if(s[i]==')') b++;
        }
        a=len/2-a;b=len/2-b;
        bool flag=0;
        int las=0;
        for(int i=0;i<len;i++)
        {
            p[0]=1;
            if(s[i]=='?')
            {
                if(a) {a--;if(i!=0)p[i]=p[i-1]+1;if(a==0) las=i;}
                else {b--;p[i]=p[i-1]-1;}
            }
            else if(s[i]=='(') p[i]=p[i-1]+1;
            else p[i]=p[i-1]-1;
        }
        for(int i=las;i<len;i++)
        {
            if(s[i]=='?'&&i!=las) break;
            if(p[i]<2)  flag=1;
        }
        if(!flag) printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}
View Code

队友给了我一个别的思路,代码更加简洁一些,但是中心思想还是两个要点:

从左到右运行的时候,设置两个计数变量num1和num2,遇到'(',num1++,遇到')',num1--,遇到'?',num2++。

遇到对于第i位,如果此时num1+num2==1,意味着此时剩下的)比?个数小1,在那么显然为了满足括号匹配条件1,所有的问号应该都是右括号,我们就可以将这一部分的计数重置为num1=1,num2=0,意味着前i项的?是固定不变的。

运行到最后,如果剩下的'('或者')'数量和'?'数量一致,那么?是固定不变的,否则括号的数量会小于问号数量,意味着问号是可以变方向的。

void solve() {
    cin >> s + 1;
    int n = strlen(s + 1);
    int wh = 0, cnt = 0;
    rep(i, 1, n) {
        if (s[i] == '(')
            wh++;
        else if (s[i] == ')')
            wh--;
        else
            cnt++;
        if (wh + cnt == 1) wh = 1, cnt = 0;
    }
    if (abs(wh) == cnt)
        cout << "YES\n";
    else
        cout << "NO\n";
}
 
View Code

D. Rorororobot

这题反而比上题思路清晰点。

题目给出的障碍物是挨着地面的,然后行动的参数是每次都行动k格。

那么我们首先要确定没有障碍物的情况下能否到达,机器人智能走上下左右四个方向,那么起点终点的坐标对k取模一定要相等。

其次由于不要求我们最低的步数,我们每次都可以将机器人升到最高的位置去跨越这些障碍,到达终点的上方,这就需要我们算出起始列到终点列的最高障碍物,可以用ST表进行维护。

注意哪个是横坐标,那个是纵坐标。。。

inline void ycl()
{
    for(int i=1;i<=18;i++)
    {
        for(int j=1;j+(1<<i)-1<=m;j++)
        {
            st[i][j]=max(st[i-1][j],st[i-1][j+(1<<(i-1))]);
        }
    }
}

inline ll search1(ll l,ll r)
{
    ll len=(ll)log2(r-l+1);
    return max(st[len][l],st[len][r-(1<<len)+1]);
}

int main()
{
    read(n);read(m);
    for(int i=1;i<=m;i++) read(a[i]),st[0][i]=a[i];
    ycl();
    read(Q);
    while(Q--)
    {
        ll x,y,xx,yy,k;
        read(x);read(y);
        read(xx);read(yy);
        read(k);
        
        if(xx%k!=x%k||yy%k!=y%k) cout<<"NO"<<endl;
        else
        {
            ll maxx=search1(min(y,yy),max(y,yy));
            ll maxm=max(x,(x%k)+n/k*k);
            if(maxm>n) maxm-=k;
            if(maxm<=maxx) cout<<"NO"<<endl;
            else cout<<"YES"<<endl;
        }        
    }
    return 0;
}
View Code

E. XOR Tree(dfs+启发式合并)

dfs+启发式合并

题目给出了一个条件:数字可以任意变换,因此我们甚至可以按位赋1的方式改变所有数字,哪怕数字很大我们也能有一个答案。

所以我们只用考虑怎么去确定最少的修改点。

如果我们去记录每个位置到根节点的异或和记为sum,对于两个节点i,j,i到j的异或和等于sum[i]xorsum[j]xora[LCA(i,j)](a为原本权值)。

当这个值为0的时候,这条路径上我们至少要改动一个权值,我们要证明改动他们的LCA节点要不劣于改动其他节点。

由于我们是dfs去做的,修改该点LCA的节点能够最大程度地解决其子树的冲突,每当我们修改了这个节点之后,我们可以直接清空这个子树,基于dfs的性质,这个子树不再会发生冲突。

假设我们dfs到了一个节点,并且记录了其未被清空的子树的所有节点的异或前缀和sun,那么如果存在两个值的sum异或等于该节点的权值,就存在冲突节点,此时就修改权值清空该子树,该过程可以用set完成,如果没有冲突,我们就进行启发式合并,将数量小的向大的合并。

注意要现将某子树的所有节点都比较完再全部合并,否则可能出现错误的最短路径导致WA。

inline void dfs(ll x,ll w)     
{
    vis[x]=1;
    bool flag=1;
    s[x].insert(w);
    for(int i=heads[x];i!=0;i=ed[i].next)
    {
        ll v=ed[i].too;
        if(!vis[v])
        {
            dfs(v,w^a[v]);
            if(s[x].size()<s[v].size()) s[x].swap(s[v]);
            for(auto lim:s[v])
            {
                if(s[x].find(lim^a[x])!=s[x].end()) {flag=0;break;}
            }
            s[x].insert(s[v].begin(),s[v].end());
            s[v].clear();
        }
    }
    if(!flag)
    {
        s[x].clear();
        ans++;
    }
    return ;
}
View Code

 

posted @ 2022-07-24 17:52  ztlsw  阅读(14)  评论(0编辑  收藏  举报