2015 Hdu 多校赛第六场

暑假过半,好想天天睡懒觉…
XM

Hdu 5355 Cake

链接

Cake

题意

有n个大小为{1,2,3…n}的蛋糕,要将这些蛋糕分给m个人,每个人所得蛋糕的大小相等,
且每个完整蛋糕只属于某一个人,即蛋糕不允许切分

分析

n个蛋糕的和sum=n*(n+1)/2,平均数avg=sum/m
因为蛋糕不能切
1.sum%m!=0 即不能整除时,肯定不能平分
2.当avg<n,也不可能平分
3.若有2m个数{1,2,3…2m}肯定可以平分,即连续2m个数肯定可以
4.可以先构造,再dfs
构造:先正向递减构造,再逆向递减构造,这样先构造2*k*m个数
则每个人手上的蛋糕肯定相等,再将剩下的数 {1,2,3…,n-2km}搜索分配
如 n=23,m=3,
第1人:23 18 17 12
第2人:22 19 16 13
第3人:21 20 15 14
剩下{1,2,3…,11} dfs 分配给这三个人

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
const int N=100010;
bool vis[N],flag;
vector<int> ans[15];
long long n,m,avg,dis,cake[N];
bool dfs(int pos,int sum)
{

if(pos>m)
return true;
for(int i=n;i>=1;i--){
if(vis[i])
continue;
if(sum+i==dis){ //若第pos人分好,再分第pos+1人
cake[i]=pos; //第i个蛋糕给第pos人
vis[i]=true;
if(dfs(pos+1,0))
return true;
vis[i]=false;
return false;
}
else if(sum+i<dis){ //若第pos人分得蛋糕少于平均值
cake[i]=pos;
vis[i]=true;
if(dfs(pos,sum+i))
return true;
vis[i]=false;
}
}
return false;
}
int main()
{

int T;
scanf("%d",&T);
while(T--){
scanf("%I64d%I64d",&n,&m);
long long sum=n*(n+1)/2;
avg=sum/m;
if(sum%m||avg<n){
printf("NO\n");
continue;
}
printf("YES\n");
int x=n%(2*m);
if(x) x=min(x+2*m,n);
dis=avg; //dis记录离平均值还差多少
while(n>x){ //先构造
dis-=n;
for(int j=1;j<=m;j++) //正向递减
ans[j].push_back(n--);
for(int j=m;j>=1;j--) //逆向递减
ans[j].push_back(n--);
dis-=n+1;
}
memset(vis,0,sizeof(vis));
memset(cake,0,sizeof(cake));
flag=false;
dfs(1,0);
for(int i=1;i<=n;i++)
ans[cake[i]].push_back(i);
for(int i=1;i<=m;i++){
int cnt=ans[i].size();
printf("%d",cnt);
for(int j=0;j<cnt;j++)
printf(" %d",ans[i][j]);
printf("\n");
ans[i].clear();
}
}
return 0;
}

Hdu 5358 First One

链接

First One

题意

给定长度为n的数组
$$ S(i,j) = \sum_{i=1}^{j} a_i $$
$求下列式子的值 ( 默认 \log_2 0 = 0) $
$$ \sum_{i=1}^{n} \sum_{j=i}^{n} (\lfloor \log_2 S(i,j) \rfloor + 1) \times (i + j) $$

分析

$ 若 2^{k-1} \leqslant x \leqslant 2^k ,则 $
$ A = \lfloor \log_2 x \rfloor + 1 = k $
$可以枚举 k,找所有满足 A=k 的区间,并求出对应式子的和$
$找A=k的区间时,可以枚举左区间 i,并找到最小的 l 符合 S(i,l)>=2^{k-1}$
$找到 最大的 r 符合 S(i,r) < 2^k,则 区间 [i,j] (l<=j<=r)的 A=k $
$其贡献值为 k*(i*(r-l+1)+(l+r)*(r-l+1)/2)$

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<stdio.h>
#include<string.h>
const int N=100010;
int n;
long long bin[40],a[N],sum[N];
void init()
{

bin[0]=1;
for(int i=1;i<=35;i++)
bin[i]=bin[i-1]<<1;
bin[0]=0;
}
long long solve()
{

long long ans=0;
for(int k=0;k<=34;k++){
long long L=bin[k],R=bin[k+1];
if(sum[n]<L) break;
int l=1,r=1;
for(int i=1;i<=n;i++){
if(sum[n]-sum[i-1]<L) break;
if(a[i]>=R) continue;
if(l<i) l=i;
if(r<i) r=i;
while(l<=n&&sum[l]-sum[i-1]<L) l++;
while(r<=n&&sum[r]-sum[i-1]<R) r++;
if(l<=r-1){
long long cnt=r-l;
ans+=(k+1)*(i*cnt+(l+r-1)*cnt/2);
}
}
}
return ans;
}
int main()
{

int T;
scanf("%d",&T);
init();
while(T--){
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%I64d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
long long ans=solve();
printf("%I64d\n",ans);
}
return 0;
}

Hdu 5360 Hiking

链接

Hiking

题意

beta准备邀请他最好的n个朋友 Hiking,
但是 只有当当前同意 Hiking的人数 不小于$ l_i,且不大于 r_i $
第i个朋友才会同意邀请,他会一个一个地邀请好友,
求最多同意Hiking的朋友人数,以及他邀请n个朋友的顺序

分析

贪心思想
先按l从小到大排序,再按r从小到大排序
对于当前满足同意邀请条件的朋友,选择最小的r邀请

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
const int N=100010;
struct stu{
int l,r,id;
}a[N];
int order[N];
bool vis[N];
vector<int> num[N];
vector<int>::iterator it;
int cmp(stu x,stu y){
if(x.l!=y.l)
return x.l<y.l;
return x.r<y.r;
}
int main()
{

int T,n;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i].l);
a[i].id=i;
}
for(int i=1;i<=n;i++)
scanf("%d",&a[i].r);
sort(a+1,a+1+n,cmp);
bool flag=true;
int i=1,ans=0;
memset(vis,0,sizeof(vis));
for(int i=0;i<=n;i++)
num[i].clear();
while(flag){
while(i<=n&&ans>=a[i].l){
num[a[i].r].push_back(a[i].id);
i++;
}
flag=false;
for(int j=ans;j<=n;j++){
if(num[j].size()){
order[ans++]=num[j][0];
vis[num[j][0]]=true;
it=num[j].begin();
num[j].erase(it);
flag=true;
break;
}
}
}
printf("%d\n",ans);
for(i=1;i<=n;i++)
if(!vis[i])
order[ans++]=i;
for(i=0;i<n-1;i++)
printf("%d ",order[i]);
printf("%d\n",order[n-1]);
}
return 0;
}

Hdu 5363 Key Set

链接

Key Set

题意

给定n个数的集合{1,2,…n},求和为偶数的非空子集的个数

分析

大小为n的集合的子集有 $2^n$ 个
和为奇数的子集个数 与和为偶数的子集个数相等
即 和为偶数的子集个数为 $2^{n-1}$,其中有一个为空集
即 和为偶数的非空子集个数为$ 2^{n-1} - 1$

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<stdio.h>
typedef long long LL;
const LL mod=1000000007;
LL powMod(LL a,LL b)
{

a%=mod;
LL ans=1;
while(b){
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main()
{

int T;
LL n;
scanf("%d",&T);
while(T--){
scanf("%I64d",&n);
printf("%I64d\n",powMod(2,n-1)-1);
}
return 0;
}