Codeforces Round #371 (Div. 1)

A:

题目大意:

在一个multiset中要求支持3种操作:

1.增加一个数

2.删去一个数

3.给出一个01序列,问multiset中有多少这样的数,把它的十进制表示中的奇数改成1,偶数改成0后和给出的01序列相等(比较时如果长度不等各自用0补齐)

题解:

1.我的做法是用Trie数来存储,先将所有数用0补齐成长度为18位,然后就是Trie的操作了。

2.官方题解中更好的做法是,直接将每个数的十进制表示中的奇数改成1,偶数改成0,比如12345,然后把它看成二进制数10101,还原成十进制是21,然后cnt[21]++,求答案的时候只要把01序列看成二进制数,然后转换成十进制x,cnt[x]就是答案。

我的代码(Trie做法)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cstdlib>
#include <set>
using namespace std;

#define X first
#define Y second
#define N 1000010
typedef long long ll;
typedef pair<int,int> pii;

int n,tot=1;
int cnt[N],ch[N][2];

void Add(int x,ll val,int k,int op)
{
    cnt[x]+=op;
    if (k==0) return;
    int v=(val%10)&1;
    if (!ch[x][v]) ch[x][v]=++tot;
    Add(ch[x][v],val/10,k-1,op);
}

int Query(int x,ll val,int k)
{
    if (k==0) return cnt[x];
    int v=(val%10)&1;
    if (!ch[x][v]) return 0;
    return Query(ch[x][v],val/10,k-1);
}

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);

    scanf("%d",&n);
    char op; ll x;
    for (int i=1;i<=n;i++)
    {
        scanf(" %c%I64d",&op,&x);
        if (op==‘+‘) Add(1,x,18,1);
        if (op==‘-‘) Add(1,x,18,-1);
        if (op==‘?‘) printf("%d\n",Query(1,x,18));
    }
    return 0;
}



B:

题目大意:

在一个n*n的矩形中,有2个没有公共边也不重叠的矩形,然后最多可以询问200次,每次询问一个矩形中有多少个矩形(会给出答案0或1或2),要求找到这两个矩形的位置。

题解:

1.如果只有一个矩形,显然可以用二分确定它的坐标。所以基本思想是找到一条分界线 将这两个矩形分开,然后各自独立寻找位置。

2.对于这两个矩形,要么是左右关系,要么是上下关系。如果是左右关系,我们先确定询问的左边界,上边界和下边界,然后逐步增大右边界,那么矩形的个数肯定是从0到1到2递增的,所以可以二分出这个 分界线。 上下关系同理。 具体实现看代码。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cstdlib>
#include <set>
using namespace std;

#define X first
#define Y second
#define Mod 1000000007
#define N 100010
typedef long long ll;
typedef pair<int,int> pii;

int xx1,yy1,xx2,yy2,X1,Y1,X2,Y2;

int Check(int x1,int y1,int x2,int y2)
{
    printf("? %d %d %d %d\n",x1,y1,x2,y2);
    fflush(stdout);
    int x; scanf("%d",&x);
    return x;
}

void Solve(int l,int d,int r,int u)
{
    int L,R,Mid,x1,y1,x2,y2;

    L=d,R=u;
    while (L<R)
    {
        Mid=(L+R)>>1;
        if (Check(l,d,r,Mid)==0) L=Mid+1;
        else R=Mid;
    }
    y2=L;

    L=d,R=u;
    while (L<R)
    {
        Mid=(L+R+1)>>1;
        if (Check(l,Mid,r,u)==0) R=Mid-1;
        else L=Mid;
    }
    y1=L;

    L=l,R=r;
    while (L<R)
    {
        Mid=(L+R)>>1;
        if (Check(l,d,Mid,u)==0) L=Mid+1;
        else R=Mid;
    }
    x2=L;

    L=l,R=r;
    while (L<R)
    {
        Mid=(L+R+1)>>1;
        if (Check(Mid,d,r,u)==0) R=Mid-1;
        else L=Mid;
    }
    x1=L;

    if (!xx1) xx1=x1,yy1=y1,xx2=x2,yy2=y2;
    else X1=x1,Y1=y1,X2=x2,Y2=y2;
}

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);

    int n,L,R,Mid;
    scanf("%d",&n);

    L=1,R=n;
    while (L<R)
    {
        Mid=(L+R)>>1;
        if (Check(1,1,Mid,n)>=1) R=Mid;
        else L=Mid+1;
    }    

    if (Check(1,1,L,n)==1 && Check(L+1,1,n,n)==1)
    {
        Solve(1,1,L,n);
        Solve(L+1,1,n,n);
    }
    else
    {
        L=1,R=n;
        while (L<R)
        {
            Mid=(L+R)>>1;
            if (Check(1,1,n,Mid)>=1) R=Mid;
            else L=Mid+1;
        }
        Solve(1,1,n,L);
        Solve(1,L+1,n,n);
    }
    printf("! %d %d %d %d %d %d %d %d\n",xx1,yy1,xx2,yy2,X1,Y1,X2,Y2);
    fflush(stdout);

    return 0;
}



C:

题目大意:

给出一个长度为n的数列(n<=3000),每次操作可以把其中一个数增大1或者减小1,求最少操作次数使得数列单调增。

题解:

1.首先一个转化:   $a_i<a_{i+1}  <--> a_i-i<=a_{i+1}-(i+1)$  所以把所有$a_i$减去$i$就转化为单调不减,就和POJ3666一样了。

具体可以看 http://www.cnblogs.com/vb4896/p/5877962.html

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cstdlib>
#include <set>
using namespace std;

#define X first
#define Y second
#define Mod 1000000007
#define N 3010
typedef long long ll;
typedef pair<int,int> pii;

inline int Mul(int x,int y){return 1ll*x*y%Mod;}
inline int Add(int x,int y){return ((x+y)%Mod+Mod)%Mod;}

int n,m;
int a[N],b[N];
ll dp[N][N],Ans=1ll<<60;

void Solve()
{
    for (int j=1;j<=m;j++) dp[1][j]=abs(b[j]-a[1]);
    for (int i=2;i<=n;i++)
    {
        ll tmp=1ll<<60;
        for (int j=1;j<=m;j++)
        {
            tmp=min(tmp,dp[i-1][j]);
            dp[i][j]=abs(b[j]-a[i]);
            dp[i][j]+=tmp;

        }
    }
    for (int j=1;j<=m;j++) Ans=min(Ans,dp[n][j]);
}

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);

    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]=a[i]-i;
    sort(b+1,b+n+1);
    for (int i=1;i<=n;i++) if (i==1 || b[i]!=b[i-1]) b[++m]=b[i];

    Solve();
    printf("%I64d\n",Ans);

    return 0;
}



D:

题目大意:

给出n*m的01矩阵,最多1e6个询问,每次询问一个子矩形中最大的全1正方形的边长。

题解

1.首先可以dp出以(i,j)为左下角的最大正方形边长,dp[i][j]=max{dp[i+1][j+1],dp[i+1][j],dp[i][j+1]}+1.当然如果(i,j)格是0 那dp[i][j]=0.

2.考虑二分答案。二分时检查矩形(x1,y1)-(x2-Mid+1,y2-Mid+1)里的dp值是否有大于等于Mid的。  区间的最大值可以用二维RMQ解决...第一次写,竟然写对了。然后想起前几天学了个用树状数组求区间最大值的方法,就想把它也扩展到二维,写到一半发现剧难写,再加上这几天心不是很静,弃疗了。

还是贴个RMQ的代码吧。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cstdlib>
#include <set>
using namespace std;

#define X first
#define Y second
#define Mod 1000000007
#define N 1010
#define Inf 1000000007
typedef long long ll;
typedef pair<int,int> pii;

int n,m,Q;
int v[N][N],dp[N][N],sum[N][N];
int f[N][N][11][11];

void Build()
{
    for (int p=0,l1=1;l1<=n;l1<<=1,p++)
    {
        for (int q=0,l2=1;l2<=m;l2<<=1,q++)
        {
            if (p+q==0) continue;
            for (int i=1;i+l1-1<=n;i++)
            {
                for (int j=1;j+l2-1<=m;j++)
                {
                    if (p!=0) f[i][j][p][q]=max(f[i][j][p-1][q],f[i+(l1>>1)][j][p-1][q]);
                    else f[i][j][p][q]=max(f[i][j][p][q-1],f[i][j+(l2>>1)][p][q-1]);
                }
            }
        }
    }
}

int Getmax(int x1,int y1,int x2,int y2)
{
    int p=0,l1=1,q=0,l2=1,tmp1,tmp2;
    while (l1<=x2-x1+1) l1<<=1,p++; p--,l1>>=1;
    while (l2<=y2-y1+1) l2<<=1,q++; q--,l2>>=1;

    tmp1=max(f[x1][y1][p][q],f[x1][y2-l2+1][p][q]);
    tmp2=max(f[x2-l1+1][y1][p][q],f[x2-l1+1][y2-l2+1][p][q]);
    return max(tmp1,tmp2);
}

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);

    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&v[i][j]);
            sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+v[i][j];
        }
    for (int i=n;i>=1;i--)
    {
        for (int j=m;j>=1;j--)
        {
            if (v[i][j]==0) dp[i][j]=0;
            else dp[i][j]=min(min(dp[i+1][j+1],dp[i][j+1]),dp[i+1][j])+1;
            f[i][j][0][0]=dp[i][j];
        }
    }

    Build();
    int L,R,Mid,x1,y1,x2,y2;
    scanf("%d",&Q);
    while (Q--)
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        if (sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]==0) printf("0\n");
        else
        {
            L=1,R=min(x2-x1+1,y2-y1+1);
            while (L<R)
            {
                Mid=(L+R+1)>>1;
                if (Getmax(x1,y1,x2-Mid+1,y2-Mid+1)>=Mid) L=Mid;
                else R=Mid-1;
            }
            printf("%d\n",L);
        }
    }

    return 0;
}



Codeforces Round #371 (Div. 1)

时间: 2024-08-04 06:43:07

Codeforces Round #371 (Div. 1)的相关文章

Codeforces Round #371 (Div. 2)B. Filya and Homework

题目链接:http://codeforces.com/problemset/problem/714/B 题目大意: 第一行输入一个n,第二行输入n个数,求是否能找出一个数x,使得n个数中的部分数加上x或部分数减去x ,n个数相等. 例如:5 1 3 3 2 1 输出: YES Init: x=1 情况,第一个数和最后一个数+x,第二三个数减去x ,则这n个数都为2(相等),故输出“YES” 解题思路: 对于刚才举的例子 可知 n个数只有三个元素 1 2 3 只要使得 2-1==3-2 即可满足条

Codeforces Round #371 (Div. 1) D. Animals and Puzzle 二维倍增

D. Animals and Puzzle 题目连接: http://codeforces.com/contest/713/problem/D Description Owl Sonya gave a huge lake puzzle of size n × m to hedgehog Filya as a birthday present. Friends immediately started to assemble the puzzle, but some parts of it turn

Codeforces Round #371 (Div. 2) C

传送门  map或者字典数的应用 简单题 题意: 思路: AC代码: 1 #include<iostream> 2 #include<cstring> 3 #include<cmath> 4 #include<map> 5 #include<algorithm> 6 #include<cstdio> 7 #define max(a,b) a>b?a:b 8 #define F(i,a,b) for(int i=a;i<=b

Codeforces Round #371 (Div. 2) A

传送门 题意: 思路: AC代码: 1 #include "iostream" 2 #include "string.h" 3 #include "stack" 4 #include "queue" 5 #include "map" 6 #include "algorithm" 7 #include "stdio.h" 8 #include "math.h&

Codeforces Round #371 (Div. 2) C. Sonya and Queries(字典树)

题目戳这 题意:给你一个数字 n ,然后 n 组输入,如果第一个字符是+,就把后面的那个数字加入到集合里面去,如果第一个字符是减号,就把后面的那个数字从集合里面去掉一个,如果是问好,就开始配对,看看有多少个数字是和问号后面的数字是匹配的,是否配对的规则是,问好后面的数字都是二进制,一表示奇数,零表示偶数,如果集合中的数字长度和二进制的输入长度不一样,就把短的那个在前面加零,比如,二进制是101,集合中的数字是12,那就是101和012匹配,如果二进制是11,集合中的数字是12345,那么就是00

Codeforces Round #371 (Div. 2) B

传送门 题意:给你串数字 任意你取一个x 对某些数进行加x 某些数进行减x操作 是否能使得数组内的数全部相等 ps:某些数里的某些可能为0 思路:先排序 然后确定数组内有多少不同的数 大于3一定不能 小于2一定能 坑点在等于3 当数组里的数成等差数列时可以 否则不可以 例如:1 4 7 x=3: 1+3 = 4: 7-3 = 4: 1 3 4 就不可以了 AC代码: 1 #include "iostream" 2 #include "string.h" 3 #inc

Codeforces Round #371 (Div. 1) D - Animals and Puzzle 二维ST表 + 二分

D - Animals and Puzzle #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PII pair<int, int> #define PLI pair<LL, int> #define ull unsigned long long using namespace std; const in

Codeforces Round #279 (Div. 2) ABCD

Codeforces Round #279 (Div. 2) 做得我都变绿了! Problems # Name     A Team Olympiad standard input/output 1 s, 256 MB  x2377 B Queue standard input/output 2 s, 256 MB  x1250 C Hacking Cypher standard input/output 1 s, 256 MB  x740 D Chocolate standard input/

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i