Codeforces Round #571 (Div. 2)

11 篇文章 0 订阅
4 篇文章 0 订阅

 A

温暖的签到

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
    LL n,m,k;
    scanf("%I64d%I64d%I64d",&n,&m,&k);
    if( min( m,k ) >= n ){
        printf("Yes");
    }else{
        printf("No");
    }
    return 0;
}

C

我们模仿KMP算法的思路,预处理子串。注意到a中相邻的两个长度为b的串的奇偶性的差异只与 子串每一位是否与前面那位 和新添加尾部的 和减少的首位 这三个元素有关。

#include <bits/stdc++.h>
using namespace std;
typedef int lint;
typedef long long LL;
int main(){
   string a,b;
   cin >> a>> b;
   lint len = b.length();
   lint flag = 0;
   for( lint i = 1;i < len;i++ ){
       if( b[i] != b[i-1] )flag^= 1;
   }
   lint flag2 = 0;
   for( lint i = 0;i < len;i++ ){
       if( a[i] != b[i] ) flag2^=1;
   }
   LL ans = 0;
   if( !flag2 ) ans++;
   lint len2 = a.length();
   for( lint i = 1;i+len <= len2;i++ ){
       flag2 ^= flag;
       if( a[i+len-1] != b[len-1] ){
           flag2 ^= 1;
       }
       if( a[i-1] != b[0] ){
           flag2 ^= 1;
       }
       if( !flag2 ) ans++;
   }
   cout << ans;
    return 0;
}

D

注意到上取整和下取整 对于总和的影响差值为1,所以我们不妨全部下取整,看加上几个1之后结果为0.

坑点:( double )floor( double ) 下取整

           ( double )ceil(double ) 上取整

          STL真好用

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef int lint;
const lint maxn = 1e5 + 1;
double a[maxn],b[maxn],c[maxn];
int main(){
    lint n;
    double sum = 0;
    scanf("%d",&n);
    for( lint i = 1;i <= n;i++ ){
        scanf("%lf",&a[i]);
        b[i] = floor(a[i]);
        c[i] = ceil(a[i]);
        sum += b[i]-a[i];
    }
    for( lint i = 1;i <= n;i++ ){
        if( abs(sum - 0) > 1e-6 ){
            sum += c[i]-b[i];
            if( c[i] > 0 ) c[i] += 0.1;
            else c[i] -= 0.1;
            printf("%I64d\n",(LL)(c[i]));
        }else{
            if( c[i] > 0 ) c[i] += 0.1;
            else c[i] -= 0.1;
            printf("%I64d\n",(LL)(b[i]));
        }
    }
    return 0;
}

E

这题好有趣啊

我最开始的想法是将要求的矩形直接拆成一些矩形的面积并,但是发现这些整块的矩形的面积不好算。只有一些特定的矩形的面积才好算。比如面积的前缀。画个图就会发现,边长为 n * 2 ^ i( i >= 1 ) 或 边长为 m * 2 ^ i ( i >= 1 )  的前缀矩形是对称的,其中1的个数为面积的一半。有了这个结论,那么问题就转化为求任意一个前缀矩形的面积,经过上述的拆分,最后余下的矩形一定在边长为 2* n * 2 * m 的矩形之中。这就好求了,还需要注意此时余下的矩形是以1为左上角还是以0为左上角的矩形。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef int lint;
const lint maxn = 2005;
lint a[maxn][maxn],sum[maxn][maxn];
vector<LL> ven,vem;
void prework( lint n,lint m ){
    for( lint i = 1;i <= n;i++ ){
        for( lint j = 1;j <= m;j++ ){
            sum[i][j] = sum[i-1][j] + sum[i][j-1]-sum[i-1][j-1];
            sum[i][j] += a[i][j];
        }
    }
    ven.push_back( n );
    for( ;n <= 1e9+1;n *= 2 ){
        ven.push_back( n );
    }
    vem.push_back( m );
    for( ;m <= 1e9+1;m*= 2 ){
        vem.push_back(m);
    }
}
LL solve( LL n,LL m,lint f ){
    if( !n || ! m ) return 0;
    lint p1 = upper_bound( ven.begin(),ven.end(),n ) - ven.begin();
    p1--;
    lint p2 = upper_bound( vem.begin(),vem.end(),m ) - vem.begin();
    p2--;
    if( p1 == -1&& p2 == -1 ){
        if( f ){
            return sum[n][m];
        }
        else return n*m - sum[n][m];
    }
    LL sum = 0;
    if( p1 >= 0 ){
        n -= ven[p1];
        sum += ven[p1]*m/2;
        return sum + solve( n,m,f^1 );
    }
    if( p2 >= 0 ){
        m -= vem[p2];
        sum += vem[p2]*n/2;
        return sum + solve( n,m,f^1 );
    }
}
int main(){
    lint n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for( lint i = 1;i <= n;i++ ){
        for( lint j = 1;j <= m;j++ ){
            scanf("%1d",&a[i][j]);
            a[i+n][j] = !a[i][j];
            a[i][j+m] = !a[i][j];
            a[i+n][j+m] = a[i][j];
        }
    }
    prework((LL)2*n,(LL)2*m);
    LL x1,x2,y1,y2;
    for( lint i = 1;i <= q;i++ ){
        scanf("%I64d%I64d%I64d%I64d",&x1,&y1,&x2,&y2);
        LL ans = solve( x2,y2,1 ) - solve( x2,y1-1,1 ) - solve( x1-1,y2,1 ) + solve( x1-1,y1-1,1 );
        printf("%I64d\n",ans);
    }
    return 0;
}

F.欧拉回路递归写法居然爆栈了。所以学到了非递归的写法。

注意到每个点最多删掉一半的边,我们新增加一个原点,将其与所有的奇度点相连接。然后跑一个欧拉回路得到一个边序列。我们把序列中下标为偶数的边删去。 一个点相邻的两边在序列中是连续的。所以每个点删掉的边数小于等于度数的一半。但是可能删掉的一半边中都是原来存在的。所以我们找到新加的边中度数为奇数的边来代替。

注意:欧拉回路的非递归写法会改变链式前向星的结构。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef int lint;
typedef pair<lint,lint> pii;
const lint maxn = 2000001;
const lint maxm = 4000001;
lint tot,ver[maxm],he[maxn],ne[maxm],id[maxm];
pii edge[maxm];
void add( lint x,lint y,lint n ){
    ver[++tot] = y;
    ne[tot] = he[x];
    he[x] = tot;
    id[tot] = n;
}
vector<lint> ans;
lint vis[maxm],v[maxn],vis2[maxm],st[maxm],re[maxm],top;
void euler( lint x ){
    st[++top] = x;
    while( top>0 ){
        lint x = st[top];
        lint cure  = he[x];
        while( cure && vis[cure] ) cure = ne[cure];
        if( cure ){
            lint y = ver[cure];
            vis[cure] = vis[cure^1] = 1;
            st[++top] = y;
            re[top] = id[cure];
            he[x] = ne[cure];
        }else{
            if( top !=  1  )ans.push_back( re[top] );
            top--;
        }
    }
}
vector<lint> res;
lint du[maxm];
int main(){
    lint n,m;
    scanf("%d%d",&n,&m);
    tot = 1;
    for( lint x,y,i = 1;i <= m;i++ ){
        scanf("%d%d",&x,&y);
        add( x,y,i );
        add( y,x,i );
        du[x]++;du[y]++;
        edge[i].first = x;
        edge[i].second = y;
    }
    lint totm = m;
    for( lint i = 1;i <= n;i++ ){
        if( du[i]&1 ){
            add( 0,i,++totm );add( i,0,totm );
        }
    }
    int res = m;
    for (int i = 0; i <= n; i++) {
        ans.clear();
        euler(i);
        for (int j = 1; j < ans.size(); j += 2) {
            int now = ans[j];
            if(now > m) vis2[now] = 1;
            else {
                int tmp = ans[j - 1];
                if(tmp> m && vis2[tmp] == 0) {
                    vis2[tmp] = 1;
                    continue;
                }
                int Next = j + 1;
                if(j == ans.size()) Next = 0;
                tmp = ans[Next];
                if(tmp > m && vis2[tmp] == 0) {
                    vis2[tmp] = 1;
                    continue;
                }
                vis2[now] = 1;
                res--;
            }
        }
    }
    printf("%d\n", res );
    for( lint i = 1;i <= m;i++ ){
        if( vis2[i] ) continue;
        printf("%d %d\n", edge[i].first,edge[i].second );
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值