【牛客小白月赛24】补题(离散化,树剖)TODO

这次比赛啥都不说了===心酸。
这次小白赛难度其实不高,除了A之外我应该都能做出来才对===心酸
FG很简单,不写题解了。A放弃了--

H 好朋友——并查集+离散化

先把所有朋友关系合并到并查集里面;然后枚举敌人关系,只要两者在一个朋友集合里面就是矛盾的。
当时只用了并查集没过,想着10^9数值的话tree数组直接用map,但是map本身内部有排序,处理速度不如数组,导致时间超时
这道题数据设计的比较强。。。只能离散化,离散化就是把一组数去重排序之后映射成下标,即

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e6 + 5;
const int N = 1e9+1;
int parent[MAXN];
//map<int,int> parent;
int find(int i) { 
    if (parent[i] != i) parent[i] = find(parent[i]);
    return parent[i];
}

void uni(int x, int y)
{
  int rootx = find(x);
  int rooty = find(y);
  if (rootx != rooty)
  {
    parent[rooty] = rootx;
  }
}

int a[MAXN],b[MAXN],c[MAXN];
vector<int> d;
signed main()
{
    int t;cin>>t;
    while(t--){
        d.clear();
        int n;cin>>n;
        for(int i=1;i<=n*2;++i) parent[i]=i;
        for(int i=0;i<n;++i){
            scanf("%d %d %d",&a[i],&b[i],&c[i]);
            d.push_back(a[i]);
            d.push_back(b[i]);
        }
        sort(d.begin(), d.end());
        d.erase(unique(d.begin(),d.end()),d.end());
        for (int i = 0; i < n; ++i) {
            a[i] = lower_bound(d.begin(), d.end(), a[i]) - d.begin() + 1,
            b[i] = lower_bound(d.begin(), d.end(), b[i]) - d.begin() + 1;
        }

        bool flag=false;
        for(int i=0;i<n;++i){
            if(c[i]==1){
                uni(a[i],b[i]);
            }
        }
        for(int i=0;i<n;++i){
           if(c[i]==0){
                int rootx = find(a[i]);
                int rooty = find(b[i]);
                if (rootx == rooty) {
                    cout<<"NO"<<endl;
                    flag=true;
                    break;
                }
            }
        }
        if(!flag) cout<<"YES"<<endl;
    }
    return 0;
}

I 求和--dfs重新编号

已知有 n个节点,有 n−1 条边,形成一个树的结构。
给定一个根节点 k,每个节点都有一个权值,节点i的权值为 vi​。
给 m个操作,操作有两种类型:
1 a x :表示将节点 a的权值加上 xxx
2 a :表示求 a 节点的子树上所有节点的和(包括 a 节点本身)

本来想直接暴力,每次查询直接dfs求距离---超时

void dfs(int u,int father){
    sum[u]=num[u];
    for(int i=h[u];i;i=ne[i])if(p[i]!=father){
        dfs(p[i],u);
        sum[u]+=sum[p[i]];
    }
}

想了想,这种动态单点修改求区间和难道不是树状数组和线段树吗?
但是树状数组要从1开始并且连续区间和阿。——自然也就想到利用dfs对整棵树重新编号,编号后,可以使得任意一颗子树的序号连续

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;

#define ll long long
const int MAXN = 1e6 + 5;
#define N 1000005
int tree[MAXN]={0};
int MAXNTREE;
inline long long query(int pos)
{
    long long ans = 0;
    while (pos > 0) {
        ans+=tree[pos];
        pos -= pos & (-pos);
    }
    return ans;
}
inline void update(int pos, int val)
{
    int ans = 0;
    while (pos <= MAXNTREE) {
        tree[pos]+=val;
        pos += pos & (-pos);
    }
}
ll tot,dis[N],fa[N],h[N],ne[N<<1],p[N<<1],wi[N<<1];
void addEdge(int u,int v,int w){p[++tot]=v;ne[tot]=h[u];h[u]=tot;wi[tot]=w;}
void add(int u,int v){p[++tot]=v;ne[tot]=h[u];h[u]=tot;}
void addOrEdge(int u,int v){ne[++tot]=h[u];h[v]=tot;}
ll num[MAXN];ll siz[MAXN]={0};
ll no[MAXN];//重新编号使得子树的节点号连续
int g_cnt=0;
void dfs(int u,int father){
    no[u]=++g_cnt;
    siz[u]=1;
    for(int i=h[u];i;i=ne[i])if(p[i]!=father){
        dfs(p[i],u);
        siz[u]+=siz[p[i]];
    }
}

signed main()
{
    int n,m,rt;cin>>n>>m>>rt;
    tot=0;
    MAXNTREE=n;
    int sum=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&num[i]);
    }
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(rt,0);
    for (int i = 1; i <= n; ++i) update(no[i], num[i]);
    for(int i=1;i<=m;++i){
        int x,u,w; scanf("%d",&x);
        if(x==1){
            scanf("%d %d",&u,&w);
            update(no[u],w);
        }else{
            scanf("%d",&u);
            printf("%lld\n", query(no[u] + siz[u] - 1) - query(no[u] - 1));
        }
    }
    return 0;
}

J 求和——前缀和


这个是真的秀。。。我太菜了 只想到了没想到可以用前缀和来算

#include <bits/stdc++.h>
#define ll long long
const int MAXN = 500005;
using namespace std;
ll a[MAXN], sum[MAXN];
const int MOD = 1e9 + 7;
int main()
{
    int n;
    scanf("%d", &n);
    assert(1 <= n && n <= 500000);
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        sum[i] = (sum[i - 1] + a[i]) % MOD;
        ans = (ans + a[i] * a[i]) % MOD;
    }
    ans = (n - 1LL) * ans % MOD;
    for (int i = 1; i <= n; i++)
        ans = (ans - 2LL * a[i] * sum[i - 1] % MOD + MOD) % MOD;
    printf("%lld\n", ans);
}

B 组队——二分

比较简单。先排序,每个数num[i]二分找到第一个大于等于num[i]+k的数位置,两者下标相减就是能取到的数。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int MAXN = 5e5 + 10;
int num[MAXN];
map<int,int> has;
signed main()
{
    int t;
    cin>>t;
    while(t--){
        int n,k;
        cin>>n>>k;
        for(int i=0;i<n;++i){
            cin>>num[i];
        }
        sort(num,num+n);
        int l=1,r=n;
        int ans=0;
        for(int i=0;i<n;++i){
           int x = lower_bound(num+i+1,num+n,num[i]+k)-num;
            //cout<<x<<endl;
            if(num[x]==num[i]+k){
                ans=max(ans,x-i+1);
            }else{
                ans=max(ans,x-i);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
全部评论

相关推荐

点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务