Codeforces Round #815 (Div. 2) (补题中)

战绩:

 

 打到一半被叫走,回来后断断续续打完的。。。

A. Burenka Plays with Fractions

刚开始感觉被trick绕进去了,思路有点乱,就先去切B了。

实际上如果要a/b=c/d,我们只用判断a*d和b*c的关系就好。

注意判断0的情况。

int main()
{
    cin>>T;
    while(T--)
    {
        cin>>a>>b>>c>>d;
        ll lima=a*d;
        ll limb=b*c;
        if(lima>limb) swap(lima,limb);
        if(lima==limb) cout<<0<<endl;
        else if(lima==0||limb==0) cout<<1<<endl;
        else if(limb%lima==0) cout<<1<<endl;
        else cout<<2<<endl;
    }
    return 0;
 } 
View Code

B. Interesting Sum

一开始没仔细读题,因为A没切出来,B就有些急。

实际上我们无论选那个区间,都能够选出最大次大最小次小四个数字。

排序计算即可。

int main()
{
    read(T);
    while(T--)
    {
        read(n);
        for(int i=1;i<=n;i++) read(a[i]);
        sort(a+1,a+1+n);
        cout<<a[n]+a[n-1]-a[1]-a[2]<<endl;
    }
    return 0;
 } 
View Code

C. Corners

只要出现了这样的0,它周围八格存在另一个0,那么有多少个1就是答案。

否则如果是单独的0,答案是1的个数-1.

否则如果没有0,个数-2.

int main()
{
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        getchar();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                a[i][j]=getchar();
            }
            getchar();
        }
        bool flag=0,flag1=0;
        ll tot=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(a[i][j]=='0') flag1=1;
                else tot++;
                if(((a[i][j]==a[i][j-1])&&(j!=1))||((a[i][j]==a[i][j+1])&&(j!=m))||((a[i][j]==a[i+1][j])&&(i!=n))||((a[i][j]==a[i-1][j])&&(i!=1))
                ||(a[i][j]==a[i+1][j+1]&&(i!=n&&j!=m))||(a[i][j]==a[i-1][j+1]&&(i!=1&&j!=m))||(a[i][j]==a[i+1][j-1]&&(i!=n&&j!=1))||(a[i][j]==a[i-1][j-1]&&(i!=1&&j!=1)))
                {
                    if(a[i][j]=='0') flag=1;
                }
            }
        }
        
        if(flag) cout<<tot<<endl;
        else if(flag1) cout<<tot-1<<endl;
        else cout<<tot-2<<endl;
    }
    return 0;
 } 
View Code

D1. Xor-Subsequence (easy version)

最基础的LIS问题可以用O(n2)的两个for循环完成,我们注意到这个关系式

(a[j]^i)<(a[i]^j)

以及a[i]的数据范围是小于200的,也就是说每个a的二进制都不超过8位,那么这个式子里两边的每个a,都只能够影响i或者j的后面8位,只要i和j的第一个差别位置在前面,那么再往前面的就没影响了。

也就是i和j的差别大于256的时候,这个关系式一定不成立。

注意位运算优先级

int main()
{
    read(T);
    while(T--)
    {
        read(n);
        for(int i=0;i<n;i++) {read(a[i]);dp[i]=1;}
        ll ans=0;
        for(int i=0;i<n;i++)
        {
            for(int j=i-1;j>=max(0,i-256);j--)
            {
                if((a[j]^i)<(a[i]^j)) dp[i]=max(dp[i],dp[j]+1);    
            }
            ans=max(dp[i],ans);
        }
        cout<<ans<<endl;
    }
    return 0;
}
View Code

 D2. Xor-Subsequence (hard version)(字典树DP)

思路来自严鸽鸽的知乎https://zhuanlan.zhihu.com/p/555425330

自己推理了一下,确实感到十分巧妙,十分感谢。

由于变化的是a数组的大小范围,我们很难去延续简单版本的思路,要考虑发现一些新的性质。

注意看这个关系式(a[j]^i)<(a[i]^j),假设左右两边的前k位都相同,第k+1位不同,那么在第k+1位有a[i]^j==1并且a[j]^i==0.

那么我们可以推出a[i]^j^a[j]^i==1,移项有a[i]^i^1==a[j]^j

但是单纯满足a[i]^i^1==a[j]^j不能表示(a[j]^i)<(a[i]^j),也就是说两者并不是充要条件的关系,因此我们还要让a[i]≠j(以上都是在第k+1为的情况)

我们使用字典树去维护这样一个东西,每一个节点代表的是a[i]^i,同时每个节点开两个数组,分别记录a[i]^i到了这个节点的同时i的在这一位是0或者1的最大DP值。

然后我们整理一下思路:

我们每次用a[i]^i对字典树进行查找,每一位我们都可以在树上找一个位相反的位置节点(存在这样的节点时,满足条件(a[i]^i^1==a[j]^j)),然后在这个节点上会有两个值,分别是j的值为0或者为1的时候,这一位保存的最大DP值,我们要满足a[i]≠j,就要取a[i]^1的位置的值进行更新。(查找答案的函数)

当我们更新完当前的a[i]^i的时候,我们要带着下标i将a[i]^i挂在树上,用下标更新经过的每个节点的这一位的DP值。(更新的函数)

这一题确实巧妙的使用了异或的性质,将难以操作的表达式,按位用两个易于操作的表达式分解。

更详细的解释可以去看严鸽鸽的知乎。

inline void insert(ll lim,ll posa)
{
    int note=0;
    for(int i=32;i>=0;i--)
    {
        int nxt=(lim>>i)&1;
        if(!Tr[note][nxt]) Tr[note][nxt]=++tot;
        note=Tr[note][nxt];
        //我们每次对这一位的i的状态进行更新. 
        F[note][(posa>>i)&1]=max(F[note][(posa>>i)&1],dp[posa]);
    }
}

inline ll looking(ll lim,ll a)
{
    ll anslim=1; 
    int note=0;
    for(int i=32;i>=0;i--)
    {
        int nxt=(lim>>i)&1;
        int too=Tr[note][nxt^1];    
        //在这一位转入另一边,此时too的节点记录着之前的a[j]^j与自己不同的位置
        //此时满足a[i]^i^1==a[j]^j 
        anslim=max(anslim,F[too][(a>>i)&1^1]+1);
        //此时满足a[i]≠j,F的第二维存入的是下标的这一位数字。 
        if(Tr[note][nxt]!=0) note=Tr[note][nxt];
        else break;
    }
    return anslim;
}

int main()
{
    read(T);
    while(T--)
    {
        for(int i=0;i<=tot;i++) Tr[i][0]=Tr[i][1]=F[i][0]=F[i][1]=0;
        tot=1;    //保持一致 
        read(n);
        for(int i=0;i<n;i++)
        {
            dp[i]=1;
            read(a[i]);
            ll lim=a[i]^i;               //在字典树上查找的值
            dp[i]=looking(lim,a[i]);
            insert(a[i]^i,i);
        }
        ll ans=0;
        for(int i=0;i<n;i++) ans=max(ans,dp[i]);
        cout<<ans<<"\n";
    }
    return 0;
}
View Code

 

posted @ 2022-08-19 11:22  ztlsw  阅读(57)  评论(0)    收藏  举报