2019牛客多校第八场

A.All-one Matrices

按行枚举(即枚举下边界)
e . g . e.g. e.g.
00000 00000 00000
00110 00110 00110
00100 00100 00100
01111 01111 01111
11110 11110 11110
以最后一行为例,首先找出每个元素向上连续1的个数: 12420 12420 12420
然后我们可以知道,区间 [ 1 , 4 ] [1,4] [1,4] [ 2 , 3 ] [2,3] [2,3] [ 3 , 3 ] [3,3] [3,3]是三个可能的答案(左边、右边、上边都已经无法拓展)所以只要判断某个可能的区间的下一行有没有0就可以了。有0说明无法拓展,即合法答案;否则说明还能够继续拓展,答案在之后的行会累积。
如何找到合法区间:可以用单调栈找出每个数右边(左边)第一个比它的元素,就能够找到合法(最长)区间,注意区间可能会重复
如何判断下一行有没有0:查询区间和是否等于区间长度即可

#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define N 3005
using namespace std;
int n,m,i,j,res,l,r,len;
char ch[N];
int a[N][N],sum[N],f[N],lfar[N],rfar[N],b[N][N];
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)
    {
        scanf("%s",ch+1);
        fo(j,1,m) a[i][j] = ch[j] - '0';
    }
    fo(i,1,n)
    {
        fo(j,1,m) sum[j] = sum[j-1] + a[i+1][j];
        stack<int> s;
        fo(j,1,m+1)
        {
            if (a[i][j] == 1) f[j]++; else f[j] = 0;
            if (s.empty() || f[j] >= f[s.top()]) s.push(j); else
            {
                while (f[s.top()] > f[j])
                {
                    rfar[s.top()] = j - 1;
                    s.pop();
                    if (s.empty()) break;
                }
                s.push(j);
            }
        }
        fo(j,1,m) f[j] -= a[i][j];
        while (!s.empty()) s.pop();
        fd(j,m,0)
        {
            if (a[i][j] == 1) f[j]++; else f[j] = 0;
            if (s.empty() || f[j] >= f[s.top()]) s.push(j); else
            {
                while (f[s.top()] > f[j])
                {
                    lfar[s.top()] = j + 1;
                    s.pop();
                    if (s.empty()) break;
                }
                s.push(j);
            }
        }
        fo(j,1,m)
        if (a[i][j] == 1)
        {
            l = lfar[j]; r = rfar[j]; len = r - l + 1;
            //if (lfar[j] == 0 || rfar[j] == 0) continue;
            if (b[l][r] == i) continue; b[l][r] = i;
            if (len != sum[r] - sum[l-1]) res++;//{res++; cout<
        }
    }
    cout<<res<<endl;
    return 0;
}

B.Beauty Values

考虑每种数字的贡献
某种数字一定会把这个序列分割成若干区间,对这些区间中的子区间的贡献为0,其他区间的贡献都为1

#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int n,x,s,i,j;
long long num,len;
int a[100005];
vector<int> v[100005];
int main()
{
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,n) v[a[i]].push_back(i);
    num = 0;
    fo(i,1,100000)
        if (v[i].size())
        {
            v[i].push_back(n+1);
            num = num + 1ll * n * (n + 1) / 2;
            s = 1;
            for (j = 0;j < v[i].size();j++)
            {
                x = v[i][j];
                len = x - s;
                num = num - len * (len + 1) / 2;
                s = x + 1;
            }
        }
    cout<<num<<endl;
    return 0;
}

E.Explorer

把体积离散化,那么最后的答案一定是这些若干段离散后的区间
然后对这些离散后的体积建立线段树,对于每一条边,在线段树上打上标记,这个标记的打法是类似于区间覆盖操作的,即在 [ 1 , 5 ] [1,5] [1,5]上打了标记就不用在 [ 1 , 3 ] [1,3] [1,3] [ 4 , 5 ] [4,5] [4,5]上打标记了
然后我们发现,对于任何一个叶子结点,从这个点到根结点上的所有标记,实际上就是这个区间所能够通过的边,那我们只需要判断 1 1 1能否走到 n n n就可以了
具体实现可以用dfs,每经过一个点就把所有的标记加入并查集,然后判断一下 1 1 1 n n n在不在同一个并查集里就可以了,注意一下这里要用到回滚操作,所以并查集不能用路径压缩而要用按秩合并
时间复杂度分析:
打标记:实际上和区间覆盖的时间复杂度是一样的,最多只会打上log个标记, O ( n l o g n ) O(nlogn) O(nlogn)
dfs:遍历一遍, O ( n l o g n ) O(nlogn) O(nlogn)
并查集:按秩合并的理论复杂度是log的,即每个标记的复杂度都是 O ( l o g n ) O(logn) O(logn),总共有 O ( n l o g n ) O(nlogn) O(nlogn)个标记, O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define N 100005
using namespace std;
struct www{int u,v,l,r;} e[N];
struct tree{int l,r;} tree[N*16];
vector<int> pt[N*16];
int f[N*2],fa[N],len[N],s1[N*20],s2[N*20],s3[N*20],L[N*4],R[N*4];
int k,n,m,i,res,snum,tot;
void build(int rt,int l,int r)
{
    if (l == r) {tree[rt].l = L[l]; tree[rt].r = R[l]; return;}
    int mid = (l + r) >> 1;
    build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
    tree[rt].l = tree[rt<<1].l; tree[rt].r = tree[rt<<1|1].r;
}
void update(int rt,int l,int r,int i,int L,int R)
{
    if (L <= tree[rt].l && tree[rt].r <= R)
    {
        pt[rt].push_back(i);
        return;
    }
    int mid = (l + r) >> 1;
    if (L <= tree[rt<<1].r) update(rt<<1,l,mid,i,L,R);
    if (tree[rt<<1|1].l <= R) update(rt<<1|1,mid+1,r,i,L,R);
}
int find(int x) {if (fa[x] == x) return x; else return find(fa[x]);}
void dfs(int rt,int l,int r)
{
    int i,t,fa1,fa2;
    for (i = 0;i < pt[rt].size(); i++)
    {
        t = pt[rt][i];
        fa1 = find(e[t].u); fa2 = find(e[t].v);
        if (fa1 == fa2) continue;
        if (len[fa1] < len[fa2])
        {
            fa[fa1] = fa2;
            snum++; s1[snum] = fa1; s2[snum] = rt; s3[snum] = -1;
        }   else
        if (len[fa1] > len[fa2])
        {
            fa[fa2] = fa1;
            snum++; s1[snum] = fa2; s2[snum] = rt; s3[snum] = -1;
        }   else
        {
            fa[fa1] = fa2; len[fa2]++;
            snum++; s1[snum] = fa1; s2[snum] = rt; s3[snum] = fa2;
        }
    }
      
    if (find(1) == find(n))
    {
        res += tree[rt].r - tree[rt].l + 1;
    }   else
    if (l != r)
    {
        int mid = (l + r) >> 1;
        dfs(rt<<1,l,mid); dfs(rt<<1|1,mid+1,r);
    }
    while (snum > 0 && s2[snum] == rt)
    {
        fa[s1[snum]] = s1[snum];
        if (s3[snum] != -1) len[s3[snum]]--;
        snum--;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,m)
    {
        scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].r);
        f[++k] = e[i].l; f[++k] = e[i].r;
    }
    sort(f+1,f+k+1);
    k = unique(f+1,f+k+1)-(f+1);
    fo(i,1,k)
    {
        if (f[i-1]+1 <= f[i]-1) {tot++; L[tot] = f[i-1]+1; R[tot] = f[i]-1;}
        tot++; L[tot] = R[tot] = f[i];
    }
    if (f[k] != 1000000000) {tot++; L[tot] = f[k] + 1; R[tot] = 1000000000;}
    //fo(i,1,tot) cout<
    build(1,1,tot);
    fo(i,1,m) update(1,1,tot,i,e[i].l,e[i].r);
    fo(i,1,n) fa[i] = i,len[i] = 1;
    dfs(1,1,tot);
    //fo(i,1,n) cout<
    cout<<res<<endl;
    return 0;
}

G.Gemstones

用栈模拟,不断从左往右把元素压入栈,如果栈顶的三个元素相同则弹出

#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
char ch[100005],stck[100005];
int n,p,i,res;
int main()
{
    scanf("%s",ch+1);
    n = strlen(ch+1);
    p = 0;
    fo(i,1,n)
    {
        p++; stck[p] = ch[i];
        if (p >= 3)
        {
            if (stck[p] == stck[p-1] && stck[p] == stck[p-2])
            {
                p--; p--; p--;
                res++;
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

你可能感兴趣的:(牛客多校)