2013 Multi-University Training Contest 4 Group(离线+BIT)

本文探讨了一种算法问题:给定1至n的排列,针对每个查询区间,如何将区间内的数进行分组以使得各组价值之和最大,并求出此时所需的最少分组数量。介绍了两种解法,包括树状数组离线维护方法和莫队算法。

Group

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2203    Accepted Submission(s): 1147


Problem Description
There are n men ,every man has an ID(1..n).their ID is unique. Whose ID is i and i-1 are friends, Whose ID is i and i+1 are friends. These n men stand in line. Now we select an interval of men to make some group. K men in a group can create K*K value. The value of an interval is sum of these value of groups. The people of same group's id must be continuous. Now we chose an interval of men and want to know there should be how many groups so the value of interval is max.
 

Input
First line is T indicate the case number.
For each case first line is n, m(1<=n ,m<=100000) indicate there are n men and m query.
Then a line have n number indicate the ID of men from left to right.
Next m line each line has two number L,R(1<=L<=R<=n),mean we want to know the answer of [L,R].
 

Output
For every query output a number indicate there should be how many group so that the sum of value is max.
 

Sample Input
1 5 2 3 1 2 5 4 1 5 2 4
 

Sample Output
1 2
 

【题意】给定1~n的一个排列,每次查询一个区间(l,r),将所求区间内的数进行分组,每一组内的数的值必须是连着的,该组的价值为元素个数的平方和 。问在区间价值最大的情况下,要分成几组 ?

【解题方法&&分析】1e5的数据,感觉莫队可以莽一下,试了一下无奈TLE。那么这道题还可以怎么做呢?我们可以这样,用树状数组离线维护,把询问按照右区间递增排序,然后从1~n维护,维护到i时注意找寻前i个是不是有a[i]-1,和a[i]+1,有的话直接去掉,因为  这两个数必定要和a[i]分到一组,留一个就好了 。

【AC 代码】

//
//Created by just_sort 2016/9/16 14:25
//Copyright (c) 2016 just_sort.All Rights Reserved
//

#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e6+7;
int a[maxn],p[maxn],ans[maxn];
struct node1{
    int l,r,id;
    bool operator<(const node1 &rhs) const{
        return r<rhs.r;
    }
}q[maxn];
struct BIT{
    int c[maxn];int n;
    void init(int _n){
        n = _n;
        memset(c,0,sizeof(c));
    }
    void add(int i,int v){
        while(i<=n){
            c[i]+=v;
            i+=i&-i;
        }
    }
    int getans(int i){
        int ret=0;
        while(i>0){
            ret+=c[i];
            i-=i&-i;
        }
        return ret;
    }
}bit;

int  main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++){
            scanf("%d",&a[i]);
            p[a[i]]=i;
        }
        for(int i=0; i<m; i++){
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q,q+m);
        bit.init(n);
        int j = 0;
        for(int i=1; i<=n; i++){
            bit.add(i,1);
            if(a[i]>1 && p[a[i]-1]<i) bit.add(p[a[i]-1],-1);
            if(a[i]<n && p[a[i]+1]<i) bit.add(p[a[i]+1],-1);
            while(j<m && q[j].r==i){
                ans[q[j].id] = bit.getans(q[j].r)-bit.getans(q[j].l-1);
                j++;
            }
        }
        for(int i=0; i<m; i++){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

【TLE 莫队】如果哪位大牛可以用莫队过,一定要告诉小弟。不过感觉这复杂度好像的确不够QAQ

//
//Created by just_sort 2016/9/16 14:25
//Copyright (c) 2016 just_sort.All Rights Reserved
//

#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e6+7;
int a[maxn],pos[maxn],vis[maxn],ans[maxn];
int block,sum;
struct node{
    int l,r,id;
    //node(int l,int r,int id):l(l),r(r),id(id){}
}q[maxn];
bool cmp(node a,node b)
{
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    else return pos[a.l]<pos[b.l];
}
void add(int x)
{
    vis[x]=1;
    sum += 1-vis[x-1]-vis[x+1];
}
void del(int x)
{
    vis[x]=0;
    sum += vis[x-1]+vis[x+1]-1;
}
int main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&n,&m);
        block = ceil(sqrt(n));
        for(int i=1; i<=n; i++){
            scanf("%d",&a[i]);
            pos[i] = i/block;
        }
        for(int i=1; i<=m; i++){
            scanf("%d%d", &q[i].l,&q[i].r);
            q[i].id = i;
        }
        sort(q+1,q+m+1,cmp);
        int L=1,R=0;
        sum = 0;
        for(int i=1; i<=m; i++){
            if(R<q[i].r){
                for(int j=R+1; j<=q[i].r; j++) add(a[j]);
            }
            else{
                for(int j=R; j>q[i].r; j--)   del(a[j]);
            }
            if(L<q[i].l){
                for(int j=L; j<q[i].l; j++)    del(a[j]);
            }
            else{
                for(int j=L-1; j>=q[i].l; j--) add(a[j]);
            }
            ans[q[i].id] = sum;
        }
        for(int i=1; i<=m; i++){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}


【解题方法2 莫队】

【代码】

const int M=100005;
int s;
struct Query{
    int L,R,id;
    void input(int x){
        scanf("%d%d",&L,&R);
        id=x;
    }
    bool operator <(const Query&tmp)const{
        if(L/s==tmp.L/s) return R<tmp.R;
        return L/s<tmp.L/s;
    }
}q[M];
int A[M],res,ans[M];
bool mark[M];
void del(int x){
    if(mark[A[x]-1]&&mark[A[x]+1]) res++;
    if(!mark[A[x]-1]&&!mark[A[x]+1]) res--;
    mark[A[x]]=0;
}
void add(int x){
    if(!mark[A[x]-1]&&!mark[A[x]+1]) res++;
    if(mark[A[x]-1]&&mark[A[x]+1]) res--;
    mark[A[x]]=1;
}
void solve(){
    int n,m;
    scanf("%d%d",&n,&m);
    s=(int)sqrt(n);
    for(int i=0;i<=n+1;i++)
        mark[i]=0;
    for(int i=1;i<=n;i++)
        scanf("%d",&A[i]);
    for(int i=0;i<m;i++)
        q[i].input(i);
    sort(q,q+m);
    int L=1,R=1;
    res=1;
    mark[A[1]]=1;
    for(int i=0;i<m;i++){
        for(;R<q[i].R;R++) add(R+1);
        for(;R>q[i].R;R--) del(R);
        for(;L<q[i].L;L++) del(L);
        for(;L>q[i].L;L--) add(L-1);
        ans[q[i].id]=res;
    }
    for(int i=0;i<m;i++)
        printf("%d\n",ans[i]);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--) solve();
    return 0;
}



用户表: Users +-------------+---------+ | Column Name | Type | +-------------+---------+ | user_id | int | | user_name | varchar | +-------------+---------+ user_id 是该表的主键(具有唯一值的列)。 该表中的每行包括用户 ID 和用户名。 注册表: Register +-------------+---------+ | Column Name | Type | +-------------+---------+ | contest_id | int | | user_id | int | +-------------+---------+ (contest_id, user_id) 是该表的主键(具有唯一值的列的组合)。 该表中的每行包含用户的 ID 和他们注册的赛事。 编写解决方案统计出各赛事的用户注册百分率,保留两位小数。 返回的结果表按 percentage 的 降序 排序,若相同则按 contest_id 的 升序 排序。 返回结果如下示例所示。 示例 1: 输入: Users 表: +---------+-----------+ | user_id | user_name | +---------+-----------+ | 6 | Alice | | 2 | Bob | | 7 | Alex | +---------+-----------+ Register 表: +------------+---------+ | contest_id | user_id | +------------+---------+ | 215 | 6 | | 209 | 2 | | 208 | 2 | | 210 | 6 | | 208 | 6 | | 209 | 7 | | 209 | 6 | | 215 | 7 | | 208 | 7 | | 210 | 2 | | 207 | 2 | | 210 | 7 | +------------+---------+ 输出: +------------+------------+ | contest_id | percentage | +------------+------------+ | 208 | 100.0 | | 209 | 100.0 | | 210 | 100.0 | | 215 | 66.67 | | 207 | 33.33 | +------------+------------+ 解释: 所有用户都注册了 208、209 和 210 赛事,因此这些赛事的注册率为 100% ,我们按 contest_id 的降序排序加入结果表中。 Alice 和 Alex 注册了 215 赛事,注册率为 ((2/3) * 100) = 66.67% Bob 注册了 207 赛事,注册率为 ((1/3) * 100) = 33.33%
最新发布
03-18
<think>好的,我需要解决这个问题。首先,我得仔细理解题目要求。题目是要统计每个赛事的用户注册百分比,保留两位小数,然后按百分比降序,如果相同的话按contest_id升序排列。给出的示例可以帮助我验证思路是否正确。 首先,我需要确定如何计算每个赛事的百分比。百分比应该是该赛事注册的用户数除以总用户数,然后乘以100,保留两位小数。总用户数来自Users表,因为每个用户可能注册多个赛事,但总用户数是固定的,不管他们注册了多少赛事。比如示例中Users表有3个用户,所以每个赛事的注册用户数除以3,再乘以100得到百分比。 那首先,我需要从Users表中获取总用户数。可以用COUNT(DISTINCT user_id)来统计总人数。然后,对于Register表中的每个contest_id,计算该赛事的注册用户数,再除以总用户数,得到百分比。 接下来是SQL的写法。可能需要子查询或者JOIN来结合这两个表的数据。不过,实际上Register表中每个contest_id对应的用户数可以用GROUP BY contest_id来计算每个赛事的用户数量。然后将这个数量除以总用户数,乘以100,然后四舍五入到两位小数。 那步骤大概是: 1. 计算总用户数:SELECT COUNT(*) FROM Users; 2. 对Register表按contest_id分组,统计每个分组的用户数; 3. 将每个分组的用户数除以总用户数,得到百分比; 4. 按百分比降序和contest_id升序排序。 需要注意的是,总用户数可能需要作为子查询或者变量来处理。例如,在SELECT语句中可能需要用到子查询来获取总用户数。 比如,可以这样写: SELECT contest_id, ROUND( (COUNT(user_id) / (SELECT COUNT(*) FROM Users)) * 100, 2 ) AS percentage FROM Register GROUP BY contest_id ORDER BY percentage DESC, contest_id ASC; 但这里有个问题,是否需要考虑重复的user_id在同一个赛事中的情况?不过根据题目,Register表的主键是(contest_id, user_id),所以每个用户在一个赛事中只能注册一次。因此,每个分组的COUNT(user_id)就是该赛事的不同用户数。而总用户数是Users表中的总人数,不管他们是否注册了任何赛事。例如,如果有用户没有注册任何赛事,总用户数仍然包括他们,但该用户不会被统计在任何赛事的注册数中。 比如示例中的Users表有3个用户,每个赛事的注册用户数是该赛事有多少不同的user_id记录,比如contest_id 207在Register表中只有user_id 2,所以注册数是1,总用户数是3,所以百分比是33.33%。 那上述的SQL是否正确? 测试一下示例的情况。示例中的Register表对于contest_id=208,有三个不同的user_id吗?根据输入: Register表中contest_id=208的有user_id=2、6、7?比如输入中的Register表: 208的条目有: 208 | 2 208 | 6 208 | 7 所以在contest_id=208的分组中,COUNT(user_id)是3。总用户数是3,所以3/3=1,即100%。和示例的输出一致。 那上述的SQL应该能正确计算。 但是,当使用COUNT(user_id)的时候,如果某个用户没有注册任何赛事,他们不会被统计进来,但总用户数仍然会包含他们。例如,如果Users表中有一个用户没有在Register表中出现,那总用户数是正确的,而每个赛事的百分比是注册用户数/总用户数,包括这个未被注册的用户。这种情况下,计算是正确的。 那现在,这个SQL是否正确? 是的。因为总用户数是来自Users表的总数,而每个赛事的注册用户数是该赛事中的用户数,所以除法是正确的。 然后,ROUND函数保留两位小数。比如在示例中的215赛事,注册用户数是2(用户6和7),总用户数是3,所以2/3=0.666666…,乘以100是66.666…,四舍五入到两位小数是66.67,符合示例。 那这个时候,这个SQL应该是正确的。然后需要将结果按percentage降序,contest_id升序排列。 但是示例的输出中,208、209、210都是100%,按contest_id降序排列吗?例如,输出中的顺序是208、209、210、215、207。但是contest_id的值是208,209,210的数值是递增的,所以按contest_id升序排列的话,这三个的排列顺序应该是208、209、210。这与示例的输出中的顺序一致。所以ORDER BY子句中的第二部分是contest_id ASC,这正确。 所以整个SQL应该是: SELECT contest_id, ROUND( (COUNT(*) * 100.0) / (SELECT COUNT(*) FROM Users), 2 ) AS percentage FROM Register GROUP BY contest_id ORDER BY percentage DESC, contest_id ASC; 这里需要注意,乘以100.0而不是100,以确保得到浮点数的结果,避免整数除法的问题。例如,在MySQL中,如果分母是整数,那么除法会得到整数部分。所以需要使用浮点数运算。 比如,在示例中的207赛事,注册用户数是1,总用户数是3。1/3是0.333…,乘以100是33.333…。保留两位小数是33.33。如果使用整数运算,可能得到33.00或者错误的数值。所以必须确保计算时使用浮点数。 因此,在SQL中,将分子或分母中的一个转换为浮点数,例如乘以100.0,或者将COUNT(*)转换为小数。 所以,正确的写法应该是: ROUND( (COUNT(user_id) * 100.0) / total_users, 2 ) 其中total_users是用户总数。或者,在MySQL中,可能不需要,但为了保险,乘以100.0是正确的。 现在,在编写这个SQL时,是否应该使用COUNT(*),或者COUNT(user_id)?在GROUP BY contest_id之后,每个分组的行数等于该赛事中的注册用户数目,因为每个用户只能注册一次同一赛事。所以COUNT(*)和COUNT(user_id)的结果是一样的。所以两种写法都可以。 因此,最终的SQL语句应该是: SELECT contest_id, ROUND( (COUNT(*) * 100.0) / (SELECT COUNT(*) FROM Users), 2 ) AS percentage FROM Register GROUP BY contest_id ORDER BY percentage DESC, contest_id; 这样应该就能得到正确的结果。然后,按照示例的输出,验证是否正确。 测试示例中的输入: Users表有3个用户。Register表中的数据: contest_id 215有用户6和7,即COUNT(*)为2。2*100/3=66.666...,四舍五入到两位小数是66.67。符合示例的输出。 contest_id 207有一个用户,计算1*100/3=33.33,正确。 其他三个赛事208、209、210的注册用户数都是3,所以3/3=100%正确。 所以SQL的输出应该与示例一致。 那这个应该就是正确的解决方案了。</think>要解决这个问题,我们需要统计每个赛事的用户注册百分比,并按特定规则排序结果。以下是详细的步骤说明和解决方案: ### 方法思路 1. **计算总用户数**:从 `Users` 表中获取所有用户的总数。 2. **统计各赛事注册用户数**:在 `Register` 表中按 `contest_id` 分组,统计每个赛事的注册用户数。 3. **计算百分比**:将每个赛事的注册用户数除以总用户数,乘以 100 并保留两位小数。 4. **排序结果**:按百分比降序排序,若百分比相同则按赛事 ID 升序排序。 ### 解决代码 ```sql SELECT contest_id, ROUND( (COUNT(*) * 100.0) / (SELECT COUNT(*) FROM Users), 2 ) AS percentage FROM Register GROUP BY contest_id ORDER BY percentage DESC, contest_id ASC; ``` ### 代码解释 1. **总用户数计算**:通过子查询 `(SELECT COUNT(*) FROM Users)` 获取 `Users` 表中的总用户数。 2. **分组统计注册用户数**:使用 `GROUP BY contest_id` 对 `Register` 表按赛事分组,`COUNT(*)` 统计每个赛事的注册用户数。 3. **计算百分比**:将每个分组的注册用户数乘以 100.0(确保浮点数运算),除以总用户数,并用 `ROUND` 函数保留两位小数。 4. **排序规则**:结果首先按百分比降序 (`percentage DESC`) 排列,若百分比相同则按赛事 ID 升序 (`contest_id ASC`) 排列。 这种方法确保准确计算各赛事的注册率,并按要求排序结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值