• Codeforces 每日一练 1213G+961E+1282B2


    (都是多解题啊QAQ,我就只写一种了,其实是不会其他的

    1213G Path Queries

    传送门
    题意:一棵节点数为n的边权树,m次询问,每次回答最大权值小于q的简单路径数目。
    考虑到符合询问的路径一定不包含权值大于q的边,可以考虑用先把询问排序,然后边排序,依次加边的离线做法。
    对于已经加的边,形成的是一个个连通块,假设某连通块的大小为n,那么它对答案的贡献就是Cn2C_{n}^{2},合并过程中先减去两个连通块的贡献,合并后加上整体的贡献。
    带权并查集+离线即可AC。

    #include <bits/stdc++.h>
    using namespace std;
    #define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    #define maxn 200005
    #define ll long long
    #define int long long
    int q[maxn],tmp[maxn],sz[maxn],f[maxn];
    struct edge{
        int u,v,w;
    }e[maxn];
    ll ans=0;
    int findfa(int x){
        if(f[x]==x)return x;
        else f[x]=findfa(f[x]);
        return f[x];
    }
    void mergefa(int a,int b){
        int fa=findfa(a),fb=findfa(b);
        if(fa!=fb){
            if(sz[fa]<sz[fb])swap(fa,fb);
            f[fb]=fa;
            ans-=sz[fa]*(sz[fa]-1)/2;
            ans-=sz[fb]*(sz[fb]-1)/2;
            sz[fa]+=sz[fb];
            ans+=sz[fa]*(sz[fa]-1)/2;
        }
    }
    bool cmp(edge a,edge b){
        return a.w<b.w;
    }
    map<int,int>mp;
    signed  main(){
        IOS
        int n,m;
        cin>>n>>m;
        for (int j = 1; j <=n ; ++j) {
            f[j]=j;
            sz[j]=1;
        }
        for (int i = 0; i <n-1 ; ++i) {
            int u,v,wt;
            cin>>u>>v>>wt;
            e[i].u=u;
            e[i].v=v;
            e[i].w=wt;
        }
        for (int i = 0; i <m ; ++i) {
            cin>>q[i];
            tmp[i]=q[i];
        }
        sort(e,e+n-1,cmp);
        sort(q,q+m);
        for (int i = 0,j=0; i <m ; ++i) {
            while(j<n-1&&e[j].w<=q[i]){
                mergefa(e[j].u,e[j].v);
                j++;
            }
            mp[q[i]]=ans;
        }
        for (int i = 0; i <m ; ++i) {
            cout<<mp[tmp[i]]<<" ";
        }
        return 0;
    }
    

    关于并查集+离线的另一道题:洛谷P1197 [JSOI2008]星球大战
    其实这道题还能点分治去写,或者二分+并查集的在线做法。
    分治走这里
    二分+并查集

    961E Tufurama

    传送门
    题意:一个电视连续剧有n季(2e5季真的折磨哦 ),每季有a[i]集,询问点对<x,y>满足第y季第x集与第x季第y集同时存在的数目。
    首先尝试把题意简化,若要满足要求一定有a[x]>=y和a[y]>=x,为了避免重复计算,加上y>x,那么就相当于在x+1~a[x]之间找满足a[y]>=x的季数,同时由于季数一定小于等于n,那么如果a[x]>=n,可以把a[x]直接换成n,把数据范围缩小到2e5。
    明白题意后就是做法了,可以树状数组也可以权值线段树,但正好没学过主席树,就去学了学,然后用主席树水过了这道题。
    (如果你像我一样没学过主席树,可以这边走:传送门
    那么我们建好树之后只需要每次去找i+1~a[i]里在区间[i,n]的个数就好。

    #include <bits/stdc++.h>
    using namespace std;
    #define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    #define maxn 200005
    #define ll long long
    int a[maxn];
    ll b[maxn];
    struct persistent_seg_tree
    {
        struct data
        {
            int sum;
            data() :sum(0) {}
        };
        struct node
        {
            int l, r;
            data d;
            node() :l(0), r(0) {}
        };
        vector<node> nodes;
        vector<int> roots;
        int sz;
    
        void up(int id)
        {
            nodes[id].d.sum = nodes[nodes[id].l].d.sum + nodes[nodes[id].r].d.sum;
        }
        int newnode(int cpy)
        {
            int id = (int)nodes.size();
            node tmp = nodes[cpy];
            nodes.push_back(tmp);
            return id;
        }
        int add(int tp, int tl, int tr, int i, int v)
        {
            int id = newnode(tp);
            if (tl + 1 >= tr)
            {
                nodes[id].d.sum += v;
                return id;
            }
            int tmid = (tl + tr) / 2;
            if (i < tmid)
            {
                int nid = add(nodes[id].l, tl, tmid, i, v);
                nodes[id].l = nid;
            }
            else
            {
                int nid = add(nodes[id].r, tmid, tr, i, v);
                nodes[id].r = nid;
            }
            up(id);
            return id;
        }
        int getsum(int tp, int tl, int tr, int l, int r)
        {
            if (l <= tl && tr <= r)
            {
                return nodes[tp].d.sum;
            }
            int tmid = (tl + tr) / 2;
            int sum = 0;
            if (l < tmid)
            {
                sum += getsum(nodes[tp].l, tl, tmid, l, r);
            }
            if (r > tmid)
            {
                sum += getsum(nodes[tp].r, tmid, tr, l, r);
            }
            return sum;
        }
        // interface
        void init(int range, int root_size) // 数组大小[0, range),操作次数
        {
            sz = range;
            nodes.clear();
            roots.clear();
            nodes.reserve(root_size * (int)(log(sz * 2.0) / log(2.0) + 1.01));
            nodes.push_back(node());
            roots.reserve(root_size + 1);
            roots.push_back(0);
        }
        void add(int pos, int v)
        {
            int last = roots.back();
            roots.push_back(add(last, 0, sz, pos, v));
        }
        int getsum(int t, int l, int r)
        {
            if (t <= 0) return 0;
            if (r < l) return 0;
            if (t >= (int)roots.size()) t = (int)roots.size() - 1;
            return getsum(roots[t], 0, sz, l, r + 1);
        }
    }tree;
    signed  main(){
        IOS
        int n;
        cin>>n;
        ll tmp=n;
        int ma=-1;
        for (int i = 1; i <=n ; ++i) {
            cin>>b[i];
            a[i]=min(b[i],tmp);
            ma=max(ma,a[i]);
        }
        tree.init(ma+1,4*n);
        for (int i = 1; i <=n ; ++i) {
            tree.add(a[i],1);
        }
        ll ans=0;
       for (int i = 1; i <=n ; ++i) {
            int r=min(n,a[i]);
            int l=i+1;
            if(r>=l)ans+=tree.getsum(r,i,n+1)-tree.getsum(l-1,i,n+1);
        }
        cout<<ans;
        return 0;
    }
    
    

    如果你不想写主席树,简单的权值线段树也是可以的,贴大佬博客了:权值线段树传送门

    1282B2 K for the Price of One (Hard Version)

    传送门
    题意:n个商品,每个加个a[i],你携带了金额为p的money,正好碰上商店的活动,购买一个商品可以选择k-1个加个小于等于它的商品免费拿走(0或k-1二选一,不能选其他数目),最后你能买多少物品。
    首先把商品按加个排序,明显买连续的前m个是最优的,假设购买了前k个,而后面的从k+2开始购买,那我们把后面的整体前移,数目不变,总价减少了,说明购买连续的前m个是最优的。
    那么就可以写dp转移了,记录买到第i个要多少钱,对于前k-1个,由于不能白嫖,只能连续买,dp[i]=dp[i-1]+a[i],而对于之后的,肯定怎么省钱怎么来,dp[i]=dp[i-k]+a[i]。转移过程中记录价格合法的最大物品数目即可。

    #include <bits/stdc++.h>
    using namespace std;
    #define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    #define maxn 200005
    #define ll long long
    ll a[maxn],p;
    int n,k;
    ll dp[maxn];
    signed  main(){
        IOS
        int t;
        cin>>t;
        while(t--) {
            cin >> n >> p >> k;
            for (int i = 1; i <= n; ++i) {
                cin >> a[i];
            }
            memset(dp,0, sizeof(dp));
            sort(a+1,a+1+n);
            for (int i = 1; i <=n ; ++i) {
                if(i<k)dp[i]=dp[i-1]+a[i];
                else dp[i]=dp[i-k]+a[i];
            }
            int ans=0;
            for (int j = 1; j <=n ; ++j) {
                if(dp[j]<=p){
                    ans=j;
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    
    
  • 相关阅读:
    String、Stringbuilder、StringBuffer之间的练习与区别
    JAVA——重点非阻塞模型:NIO模型及IO操作步骤
    JAVA——IO模型分类
    SpringBoot(六) SpringBoot使用汇总(持续更新)
    Java高级(一) Future机制
    SpringBoot(二) 事务
    Java8(七) 新的DateTime API
    Java8(六) Optional
    Java8(五) 接口默认方法
    Java8(四) StreamAPI
  • 原文地址:https://www.cnblogs.com/Bazoka13/p/12722304.html
Copyright © 2020-2023  润新知