Google Kick Start 2020 Round C

Countdown

题意

给一个长为n的数组,一个数k,求数组中有多少个子串组成k,k-1,k-2....2,1

思路

扫一遍记录当前期望的值就行了。

代码

#include<bits/stdc++.h>

using namespace std;

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int n,k,x,cur,res=0;
        scanf("%d%d",&n,&k);
        cur=k;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            if(x!=cur)cur=k;
            if(x!=cur)continue;
            else if(x==1)res++,cur=k;
            else cur=cur-1;
        }
        printf("Case #%d: %d\n",++cas,res);
    }
}

Stable Wall

题意

给若干(不多于26)块由1*1方块组成的异形砖块,然后拼成一个矩阵,当作墙,如果存在一种按顺序把每一块砖拼上去,使得拼的过程中每一块底部都有支撑,那么认为这堵墙是稳定的,否则不稳定。现在给出这个矩阵,由字母代表砖块的形状,求是否稳定。

思路

拓扑排序,遍历矩阵寻找不同砖块之间的依赖关系。再注意细节的处理就好了

代码

#include<bits/stdc++.h>

using namespace std;

char s[50][50];
vector<int> G[30];
vector<int> res;
int in[30];
bool vis[30],vis1[30];

void topo(int s)
{
    queue<int> Q;
    Q.push(s);
    vis[s]=1;
    res.push_back(s);
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        for(int i=0; i<G[u].size(); i++)
        {
            int v=G[u][i];
            if(!vis[v]&&(!--in[v]))
            {
                Q.push(v);
                vis[v]=1;
                res.push_back(v);
            }
        }
    }
}

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int r,c,n=0;
        scanf("%d%d",&r,&c);
        for(int i=0;i<30;i++)
            G[i].clear();
        res.clear();
        memset(vis,0,sizeof vis);
        memset(vis1,0,sizeof vis1);
        memset(in,0,sizeof in);
        for(int i=0;i<r;i++)
        {
            scanf("%s",s[i]);
            for(int j=0;j<c;j++)
            {
                int u=s[i][j]-'A';
                if(!vis1[u])n++;
                vis1[u]=1;
            }
        }
        for(int i=0;i<r-1;i++)
            for(int j=0;j<c;j++)
                if(s[i][j]!=s[i+1][j])
                {
                    int u=s[i+1][j]-'A',v=s[i][j]-'A';
                    if(u==v)continue;
                    G[u].push_back(v);
                }
        for(int i=0;i<26;i++)
        {
            sort(G[i].begin(),G[i].end());
            G[i].erase(unique(G[i].begin(),G[i].end()),G[i].end());
            for(int j=0;j<G[i].size();j++)
                in[G[i][j]]++;
        }
        for(int i=0;i<26;i++)
            if(!in[i]&&vis1[i]&&!vis[i])
                topo(i);
        printf("Case #%d: ",++cas);
        if(res.size()!=n)
            printf("-1\n");
        else
        {
            for(int i=0;i<res.size();i++)
            printf("%c",res[i]+'A');
            printf("\n");
        }
    }
}

Perfect Subarray

题意

给一个长为n的数组,元素在[-100,100]范围内,求数组有多少子串满足子串之和是一个完全平方数(0,1,4,9,16....

思路

考虑从左到右遍历数组,然后考虑以当前i结尾的子串有多少完全平方数。最暴力的想法,每遍历到一位i,可以枚举前面每一位j,判断寻找j-i是否是完全平方数。

但是这样时间复杂度为O(n^2),显示不能接受。想到额外条件,数组元素范围只有100,那么可以想到,子串之和带来的完全平方数不会很大,即使全部取最大值100,那么最大的完全平方数也只有1e7大小,也就是说子串和为完全平方数的可能的取值最多3100多种。

那么可以想到每遍历到一位i,遍历所有可能的完全平方数取值,然后对于每一个可能的完全平方数取值q,统计有多少j,使得j-isumq

可以用前缀和优化快速统计j,记当前1-i的和为tot,现在要使得j-i的和为q,那么就等价于1-j的和为tot-q,那么只需要统计有多少j,j<i,使得1-j的和为tot-q

正好可以在遍历过程中用哈希表t[s]记录前缀和为s的前缀和的个数。对于每一位i,可以先统计答案,再用当前的值更新哈希表。这样就可以保证j<i

代码

#include<bits/stdc++.h>

using namespace std;

const int MAX=1e5+5;

int a[MAX];

unordered_map<int,int>mp;

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        mp.clear();
        int n,sum=0,maxx=-200;
        long long res=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]),maxx=max(maxx,a[i]);
        int m=sqrt(maxx*n)+5;
        mp[0]=1;
        int minn=0;
        for(int i=0;i<n;i++)
        {
            sum+=a[i];
            for(int j=0;j<m;j++)
            {
                int s=j*j;
                if(sum-s<minn)break;
                res+=mp[sum-s];
            }
            mp[sum]++;
            minn=min(minn,sum);
        }
        printf("Case #%d: ",++cas);
        printf("%lld\n",res);
    }
}

Candies

题意

给长为n的数组aq个询问,每个询问要不更改某一位的值,要不求l-r区间的sum

sum定义如下:

\[sum=\sum_{i=l}^r(-1)^{i-l}a[i] \]

最后求所有查询的sum的和

思路

很典型的线段树风格的题目。修改很简单,区间sum需要考虑一下。直接拿官方题解的图。

img

整体的阶梯是1-nsum,可以看到5-8的答案是蓝色部分,是包含在1-nsum当中的。

先按1-nsum计算方法建一棵树T1,再正负交替建立一棵树T0,以5-8为例,查询的答案就是T1[5-8]-T0[5-8]*4,那么只需要维护T0 T1两棵线段树就可以了

代码

#include<bits/stdc++.h>

using namespace std;

const int MAX=2e5+5;

struct data
{
    long long sum;
}tree[2][MAX<<2];//0 for -1^i;  1 for -1^i*i
int a[MAX];

void pushup(int rt)
{
    int ls=rt<<1,rs=rt<<1|1;
    tree[0][rt].sum=tree[0][ls].sum+tree[0][rs].sum;
    tree[1][rt].sum=tree[1][ls].sum+tree[1][rs].sum;
}

void build(int l,int r,int rt)
{
    if(l==r)
    {
        int factor=l&1?1:-1;
        tree[1][rt].sum=1ll*a[l]*factor*l;
        tree[0][rt].sum=1ll*a[l]*factor;
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}

void update_point(int P,int C,int l,int r,int rt)
{
    if(l==r)
    {
        int factor=l&1?1:-1;
        tree[1][rt].sum=1ll*C*factor*l;
        tree[0][rt].sum=1ll*C*factor;
        return ;
    }
    int m=(l+r)>>1;
    if(P<=m)
        update_point(P,C,l,m,rt<<1);
    else
        update_point(P,C,m+1,r,rt<<1|1);
    pushup(rt);
}

long long query(int L,int R,int l,int r,int rt,int treenum)
{
    if(L<=l&&r<=R)
    {
        return tree[treenum][rt].sum;
    }
    long long ans=0;
    int m=(l+r)>>1;
    if(L<=m)
        ans+=query(L,R,l,m,rt<<1,treenum);
    if(R>m)
        ans+=query(L,R,m+1,r,rt<<1|1,treenum);
    return ans;
}

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int n,q;
        long long sum=0;
        char s[5];
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        build(1,n,1);
        while(q--)
        {
            int x,y;
            scanf("%s%d%d",s,&x,&y);
            if(s[0]=='Q')
            {
                long long cur=0;
                cur=query(x,y,1,n,1,1);
                cur-=query(x,y,1,n,1,0)*(x-1);
                if(x%2==0)cur*=-1;
                sum+=cur;
            }
            if(s[0]=='U')
                update_point(x,y,1,n,1);
        }
        printf("Case #%d: %lld\n",++cas,sum);
    }
}
posted @ 2022-10-13 23:02  cryingrain  阅读(22)  评论(0编辑  收藏  举报