iwtgm-13

题目链接

A.

自己写的时候固定思维左右转换,上下转换,还分开求了最短子序列,从一开始就错了...

正解:
因为操作可以变成4个方向的任意一个,相当于有几个字符就可以有几个任意的操作
二分区间长度,for循环滑动窗口枚举子序列
计算部分用前缀和维护求出最终的x,y
计算x,y和终点的xx,yy还差多少步,若区间长度大于等于步数,且多出来的步数是偶数(来回抵消用)就满足条件

逐字符敲还是不对,所以就复制粘贴了

void solve() {
    int n, sx, sy;
    string s;
    cin >> n >> s >> sx >> sy;
    s = " " + s;
    vector<int> u(n + 1), d(n + 1), r(n + 1), l(n + 1);
    for(int i = 1; i < s.size(); i++) {
        if(s[i] == 'U')u[i]++;
        u[i] += u[i - 1];
        if(s[i] == 'D')d[i]++;
        d[i] += d[i - 1];
        if(s[i] == 'R')r[i]++;
        r[i] += r[i - 1];
        if(s[i] == 'L')l[i]++;
        l[i] += l[i - 1];
    }
    auto check = [&](int mid) {
        for(int i = 1; i + mid - 1 <= n; i++) {
            int U = u[i - 1] + u[n] - u[i + mid - 1];
            int D = d[i - 1] + d[n] - d[i + mid - 1];
            int R = r[i - 1] + r[n] - r[i + mid - 1];
            int L = l[i - 1] + l[n] - l[i + mid - 1];
            int x = R - L;
            int y = U - D;
            // debug2(x, y);
            int res = abs(sx - x) + abs(sy - y);
            // debug2(res, mid);
            if(res <= mid && (mid - res) % 2 == 0)return true;
        }
        return false;
    };
    int li = 0, ri = n, ans = -1;
    while(li <= ri) {
        int mid = li + ri >> 1;
        if(check(mid))ri = mid - 1, ans = mid;
        else li = mid + 1;
    }
    cout << ans << endl;
}

B.

把除最后一行和最后一列以外的所有数都置为0
任何数异或0还等于这个数,这样一来既不影响答案,要考虑的也少了许多
然后最后一行除最后一个分别置为b[0],b[1]...
最后一列除最后一个数分别置为a[0],a[1],a[2]...
现在考虑最后一个数,它必须满足:(a[1]b[1]b[2]...b[m-1])=(b[m]a[2]a[3]...a[n])
事实上,它确实满足
异或有一个性质:若ax=b,那么a=bx
(a[1]a[2]a[3]...a[n])=(b[1]b[2]b[3]...b[m])
把a[1]代入到第一个式子,成立

int n,m,a[105],b[105];
int ans[105][105];
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++)cin>>b[i];
    int cnt1=0,cnt2=0;
    for(int i=1;i<=n;i++)cnt1^=a[i];
    for(int i=1;i<=m;i++)cnt2^=b[i];
    if(cnt1!=cnt2){
        cout<<"NO";return ;
    }
    cout<<"YES"<<endl;
    for(int i=1;i<n;i++){
        for(int j=1;j<m;j++){
            ans[i][j]=0;
        }
    }
    for(int i=1;i<n;i++)ans[i][m]=a[i];
    for(int i=1;i<m;i++)ans[n][i]=b[i];
    for(int i=1;i<n;i++)b[m]^=a[i];
    ans[n][m]=b[m];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cout<<ans[i][j]<<' ';
        }
        cout<<endl;
    }
}

C.

两种状态
0,0,1,1,2,2递增这样的
0,0,1,1,3,3,1,1,3,3这样的,一旦不是以1递增,这里2没有出现,后面就只能1和3反复出现了
dp[n][0/1]表示第一种和第二种
这道题的dp跟正常的dp不太一样,有一点桶的性质
考虑的不是递推,是这个字符加进来,有哪些情况可以被改变和转移
然后第二种情况按照dp的意思是0,0,1,1,1,1,3,3,3,3

用正常的dp递推写这道题是非常复杂的,我还理解不了,用这种桶的方式计数较为方便

ll dp[N][2];
const int mod=998244353;
void solve()
{
    int n;cin>>n;
    for(int i=0;i<=n+1;i++){
        dp[i][0]=0;dp[i][1]=0;
    }
    dp[0][0]=1;//初始化,因为0和1可以作为开头
    for(int i=1;i<=n;i++){
        int x;cin>>x;x++;//加1操作后会比较方便,因为1可以作为开头,但它后面就只能一直取1,因为比它小2的是负数,取不了,它相当于是第二种情况
        dp[x][0]+=dp[x][0]+dp[x-1][0];//等号右边的第一项是当前字符已经存在的所有情况,是把x加在这些情况后面。第二项是当前字符直接加在x-1后面,相当于重复的第一个
        dp[x][1]+=dp[x][1];//当前字符是第二种情况,可以加一倍
        dp[x+2][1]+=dp[x+2][1];//就是在原来的所有情况上加上这个字符,写成[x+2]是桶的性质,可以这样等价,所以说是不大正常的dp
        if(x>1)dp[x][1]+=dp[x-2][0];//把当前的字符当作第二种情况的开头,所以x-1是第一种情况的时候,也就是说,第二种情况的0,1,3,1 其实可以等价转化为:0,1,1,3,用桶计数
        dp[x][0]%=mod;
        dp[x][1]%=mod;
        dp[x+2][1]%=mod;
    }
    ll ans=0;
    for(int i=1;i<=n+1;i++){
        ans+=dp[i][0]+dp[i][1];
        ans%=mod;
    }
    cout<<ans<<endl;
}
posted @ 2023-11-05 22:10  WW爆米花  阅读(5)  评论(0编辑  收藏  举报