BC#90 解题报告

33 篇文章 0 订阅
29 篇文章 0 订阅

A

题意

给出一堆旗子的坐标,问有多少个行和列没有旗子。
坐标范围 106 ,个数 106

题解

搞2个bool数组就行了…
排序被卡,gg

B

题意

一个有根树,每个节点上都有若干个石子,每次操作可以将一个节点上的若干个石子 (>0) 移动到它的父亲上,当一个人没有任何合法操作时则负,判断当期局面是必胜态还是必败态。

题解

设根节点的深度为0,将所有深度为奇数的节点的石子数目xor起来,则先手必胜当且仅当这个xor和不为0。 证明同阶梯博弈。对于偶深度的点上的石子,若对手移动它们,则可模仿操作;对于奇深度上的石子,移动一次即进入偶深度的点。 时空复杂度 O(n)

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 100005
int T,n,x,ans;
int a[N];
int tot,point[N],nxt[N*2],v[N*2];
void clear()
{
    ans=0;
    tot=0;memset(point,0,sizeof(point));
}
void add(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void dfs(int x,int fa,int dep)
{
    if (dep%2) ans^=a[x];
    for (int i=point[x];i;i=nxt[i])
        if (v[i]!=fa)
            dfs(v[i],x,dep+1);
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        clear();
        scanf("%d",&n);
        for (int i=1;i<n;++i)
        {
            scanf("%d",&x);
            add(x,i);add(i,x);
        }
        for (int i=0;i<n;++i) scanf("%d",&a[i]);
        dfs(0,-1,0);
        if (!ans) puts("lose");
        else puts("win");
    }
}

C

题意

一列数,每次操作有可能将一种数替换成另一种数,也可能查询一段区间内的颜色段数。
105

题解

和梦幻布丁那道题很像,替换用链表的启发式合并就可以完成,查询的话需要动态维护颜色段数的前缀和,所以再加一个bit。
需要注意的是,每一次都只将小的链表里的颜色改变,所以要加一个数组来记录“等价颜色”。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 100005
#define C 1000005
int T,n,q,Max,opt,x,y,color,ans;
int a[N],nxt[N],head[C],tail[C],cnt[C],f[C],s[N];
int c[N];
void clear()
{
    Max=0;
    memset(nxt,0,sizeof(nxt));memset(head,0,sizeof(head));memset(tail,0,sizeof(tail));
    memset(cnt,0,sizeof(cnt));memset(f,0,sizeof(f));memset(s,0,sizeof(s));
    memset(c,0,sizeof(c));
}
void add(int loc,int val)
{
    for (int i=loc;i<=n;i+=i&(-i))
        c[i]+=val;
}
int query(int loc)
{
    int ans=0;
    for (int i=loc;i>=1;i-=i&(-i))
        ans+=c[i];
    return ans;
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        clear();
        scanf("%d%d",&n,&q);
        for (int i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            Max=max(Max,a[i]);s[i]=s[i-1];
            if (a[i]!=a[i-1]) s[i]++;
            if (tail[a[i]]==0) head[a[i]]=i;
            nxt[i]=tail[a[i]];
            tail[a[i]]=i;
            cnt[a[i]]++;
        }
        for (int i=1;i<=n;++i) add(i,s[i]-s[i-1]);
        for (int i=1;i<=Max;++i) f[i]=i;
        for (int i=1;i<=q;++i)
        {
            scanf("%d",&opt);
            if (opt==1)
            {
                scanf("%d%d",&x,&y);
                if (f[x]==f[y]) continue;
                if (cnt[f[x]]>cnt[f[y]]) swap(f[x],f[y]);
                x=f[x],y=f[y];
                if (cnt[x]==0) continue;
                color=y;
                for (int now=tail[x];now;now=nxt[now])
                {
                    if (a[now-1]==color)
                        add(now,-1);
                    if (a[now+1]==color)
                        add(now+1,-1);
                }
                for (int now=tail[x];now;now=nxt[now]) a[now]=color;
                nxt[head[x]]=tail[y],tail[y]=tail[x],cnt[y]+=cnt[x];
                head[x]=tail[x]=cnt[x]=0;
            }
            else
            {
                scanf("%d%d",&x,&y);
                ans=query(y)-query(x-1);
                if (a[x]==a[x-1]) ++ans;
                printf("%d\n",ans);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值