2019 浙江省赛部分题解(The 16th Zhejiang Provincial Collegiate Programming Contest Sponsored by TuSimple)

Problem A

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4100

题意:

以连通块中元素个数为权值,建立线段树,维护三个变量,分别是cnt(这样的连通块的数量),sum(连通块的总的点数),pfh(各连通块之间点数的平方和)(注意不是和的平方)

cnt代表连通块数量,tot_ret代表当前状态下的图,在不增长连通块个数的前提下,可以加的边数。

所以对于最小值很显然。

对于最大值,首先减去块内连的边,然后去线段树查询剩下的边怎么加,首先填连通块元素大小大的,这样也就是类似查询第k大,查到叶子结点,再判断
块中元素为tree[cur].l的 块 我需要多少个 返回回去,就行了。

PS:卡cin
题解:权值线段树 + 并查集 + 二分

C++版本一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int>PII;
const int MAX = 2e5 + 10;
int n, q,cnt,f[MAX];

ll tot_ret,ret[MAX],num[MAX];

int get(int v)
{
   
    return f[v] == v ? v : f[v] = get(f[v]);
}

struct node
{
   
    int l,r ;
    ll cnt, sum, pfh;
} tree[MAX << 2];

void pushup(int cur)
{
   
    tree[cur].cnt = tree[cur << 1].cnt + tree[cur << 1|1].cnt;
    tree[cur].sum = tree[cur << 1].sum + tree[cur << 1|1].sum;
    tree[cur].pfh = tree[cur << 1].pfh + tree[cur << 1|1].pfh;

}

void build(int l, int r, int cur)
{
   
    tree[cur].l = l;
    tree[cur].r = r;
    if(l == r)
    {
   
        tree[cur].pfh = tree[cur].sum = tree[cur].cnt = (l == 1? n : 0);
        return ;
    }
    int mid = (l + r) >> 1;
    build(l,mid,cur << 1) ;
    build(mid+1,r,cur << 1|1) ;
    pushup(cur);
}

void update(int cur, ll tar, ll val)
{
   
    if(tree[cur].l == tree[cur].r)
    {
   
        tree[cur].cnt += val;
        tree[cur].sum += val * tar;
        tree[cur].pfh += val * tar * tar;
        return ;
    }
    int mid = (tree[cur].l + tree[cur].r) >> 1;
    if(tar <= mid) update(cur << 1, tar, val);
    else update(cur << 1|1,tar,val);
    pushup(cur);
}

ll query(int cur, ll k, int sum)
{
   
    if(tree[cur].l == tree[cur].r)
    {
   
        ll l = 1,r = tree[cur].cnt,mid;
        mid = (l + r) >> 1;
        while(l < r)
        {
   
            mid = (l + r) >> 1;
            if(mid * (mid - 1)/2*tree[cur].l* tree[cur].l + mid * tree[cur].l * sum >= k)r =mid;
            else l = mid + 1;

        }
        return l;
    }
    int mid = (tree[cur].l + tree[cur].r) >> 1;
    ll tmp = (tree[cur << 1 | 1].sum * tree[cur << 1 | 1].sum - tree[cur << 1 | 1].pfh)/2 + tree[cur << 1 | 1].sum * sum;
    if(tmp < k)
        return tree[cur << 1 | 1].cnt + query(cur << 1,k - tmp,sum + tree[cur<<1 | 1].sum);
    else return query(cur << 1 |1,k,sum);
}

ll cal(ll k)
{
   
    if(k <= tot_ret) return cnt ;
    k -= tot_ret;
    ll tmp = query(1,k,0);
    return cnt - tmp + 1;
}

int main()
{
   
    //ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    int t ;
    cin >> t;

    while(t--)
    {
   
        		scanf("%d%d",&n,&q);

        //cin >> n >> q;
        for(int i = 1; i <= n ; i++)
            f[i] = i, num[i] = 1,ret[i] = 0;
        tot_ret = 0,cnt = n;
        build(1,n,1);
        while(q--)
        {
   
            int op;
            scanf("%d",&op);
            //cin >> op;
            if(op == 1)
            {
   
                int a, b;
                scanf("%d %d",&a,&b);
                //cin >> a >> b;
                a= get(a), b = get(b);
                if(a == b)
                {
   
                    ret[a] --;
                    tot_ret--;
                    continue ;
                }
                if(num[a] > num[b]) swap(a,b);
                update(1,num[a],-1);
                update(1,num[b],-1);
                f[a] = b;
                tot_ret += num[a] * num[b] - 1;
                cnt--;
                ret[b] = ret[a] + ret[b] + num[a] * num[b] - 1;
                num[b] += num[a];
                ret[a] = num[a] = 0;
                update(1,num[b],1);
            }
            else
            {
   
                ll k ;
                scanf("%lld",&k);
               // cin >> k;
                ll minn = max(1LL, cnt - k);
                ll maxx = cal(k);
                printf("%lld %lld\n",minn,maxx);
                //cout << minn << " " << maxx << endl;
            }
        }
    }
    return 0;
}


C++版本二
在这里插入图片描述
原博客

#include<bits/stdc++.h>
using namespace std;
#define mid (l+r)/2
int i,i0,n,m,T,pre[100005],siz[100005];
long long sum,num;
int fin(int x){
   return (pre[x]==x)?x:pre[x]=fin(pre[x]);}
void uni(int x,int y){
   if(fin(x)!=fin(y))pre[fin(y)]=fin(x);}
struct node
{
   
    long long siz,siz2,siz3;
}tree[400005];
node operator+(node a,node b){
   return {
   a.siz+b.siz,a.siz2+b.siz2,a.siz3+b.siz3};}
void b_tree(int l,int r,int p)
{
   
    if(l==r&&l==1)tree[p].siz=tree[p].siz3=n,tree[p].siz2=0;
    else tree[p].siz=tree[p].siz2=tree[p].siz3=0;
    if(l!=r)b_tree(l,mid,p*2),b_tree(mid+1,r,p*2+1);
}
void add_tree(int l,int r,int p,int a)
{
   
    if(l==r)tree[p].siz+=l,tree[p].siz2+=l*(l-1)/2,tree[p].siz3++;
    else
    {
   
        if(a<=mid) add_tree(l,mid,p*2,a);
        else if(a>=mid+1)add_tree(mid+1,r,p*2+1,a);
        tree[p]=tree[p*2]+tree[p*2+1];
    }
}
void erase_tree(int l,int r,int p,int a)
{
   
    if(l==r)tree[p].siz-=l,tree[p].siz2-=l*(l-1)/2,tree[p].siz3--;
    else
    {
   
        if(a<=mid) erase_tree(l,mid,p*2,a);
        else if(a>=mid+1)erase_tree(mid+1,r,p*2+1,a);
        tree[p]=tree[p*2]+tree[p*2+1];
    }
}
int q_tree(int l,int r,int p,long long k,long long v)
{
   
    if(l==r)
    {
   
        int ll=1,rr=tree[p].siz3;
        while(ll<rr)
        {
   
            int mmid=(ll+rr)/2;
            if((v+mmid*l)*(v+mmid*l-1)/2>=l*(l-1)/2*mmid+k)rr=mmid;
            else ll=mmid+1;
        }
        return ll;
    }
    else
    {
   
        if((v+tree[p*2+1].siz)*(v+tree[p*2+1].siz-1)/2>=k+tree[p*2+1].siz2)return q_tree(mid+1,r,p*2+1,k,v);
        else return q_tree(l,mid,p*2,k+tree[p*2+1].siz2,v+tree[p*2+1].siz)+tree[p*2+1].siz3;
    }
}
int main()
{
   
    scanf("%d",&T);
    while(T--)
    {
   
        scanf("%d %d",&n,&m);
        for(i=1;i<=n;i++)pre[i]=i,siz[i]=1;
        sum=0,num=n;
        b_tree(1,n,1);
        for(i=1;i<=m;i++)
        {
   
            int op;
            scanf("%d",&op);
            if(op==1)
            {
   
                int x,y;
                scanf("%d %d",&x
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值