czy的后宫3(莫队)

【题目描述】

上次czy在机房妥善安排了他的后宫之后,他发现可以将他的妹子分为c种,他经常会考虑这样一个问题:在[l,r]的妹子中间,能挑选出多少不同类型的妹子呢?

注意:由于czy非常丧尸,所以他要求在所挑选的妹子类型在[l,r]中出现次数为正偶数,你懂得。

问题简述:n个数,m次询问,每次问[l,r]区间有多少个数恰好出现正偶数次

【输入格式】

第一行3个整数,表示n,c,m

第二行n个数,每个数Ai在[1,c]之间,表示一个Ai类型的妹子

接下来m行,每行两个整数l,r,表示询问[l,r]这个区间的答案

【输出格式】

有m行,表示第i次询问的答案

【样例输入】

5 5 3

1 1 2 2 3

1 5

3 4

2 3

【样例输出】

2

1

0

【数据范围】

共有10组测试数据

1-4组n,m=500,2000,5000,10000,c=1000

5-7组n,m=20000,30000,40000,c=10000

8-10组n,m=50000,80000,100000,c=100000

数据保证随机生成

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=;
int n,m,c,vis[maxn],a[maxn],l,r,ans;
int main(){
//freopen("Cola.txt","r",stdin);
freopen("harem.in","r",stdin);
freopen("harem.out","w",stdout);
scanf("%d%d%d",&n,&c,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
for(int i=;i<=m;i++){
scanf("%d%d",&l,&r);ans=;
memset(vis,,sizeof(vis));
for(int j=l;j<=r;j++)vis[a[j]]++;
for(int j=;j<=c;j++)
if(vis[j]%==&&vis[j])ans++;
printf("%d\n",ans);
}
}

40分 暴力(我以为只能得20分)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=;
struct node{
int l,r,ans,id;
}q[maxn];
int cnt[maxn],a[maxn],belong[maxn],n,m,c;
int cmp1(node x,node y){
if(belong[x.l]!=belong[y.l])
return belong[x.l]<belong[y.l];
return x.r<y.r;
}
int cmp2(node x,node y){
return x.id<y.id;
}
int main(){
//freopen("Cola.txt","r",stdin);
freopen("harem.in","r",stdin);
freopen("harem.out","w",stdout);
scanf("%d%d%d",&n,&c,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
int block=(int)(sqrt(n));
for(int i=;i<=n;i++)belong[i]=(i-)/block+;
for(int i=;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+,q+m+,cmp1);
int ans=,l=,r=;
for(int i=;i<=m;i++){
while(l>q[i].l){
l--;cnt[a[l]]++;
if(cnt[a[l]]%==)ans++;
if(cnt[a[l]]%==&&cnt[a[l]]!=)ans--;
}
while(r<q[i].r){
r++;cnt[a[r]]++;
if(cnt[a[r]]%==)ans++;
if(cnt[a[r]]%==&&cnt[a[r]]!=)ans--;
}
while(l<q[i].l){
cnt[a[l]]--;
if(cnt[a[l]]%==&&cnt[a[l]]!=)ans++;
if(cnt[a[l]]%==)ans--;
l++;
}
while(r>q[i].r){
cnt[a[r]]--;
if(cnt[a[r]]%==&&cnt[a[r]]!=)ans++;
if(cnt[a[r]]%==)ans--;
r--;
}
q[i].ans=ans;
}
sort(q+,q+m+,cmp2);
for(int i=;i<=m;i++){
printf("%d\n",q[i].ans);
}
}

100分 莫队

czy的后宫4  (dp)

【问题描述】

czy有很多妹子,妹子虽然数量很多,但是质量不容乐观,她们的美丽值全部为负数(喜闻乐见)。

czy每天都要带N个妹子到机房,她们都有一个独一无二的美丽值,美丽值为-1到-N之间的整数。他想要把这些妹子排成一个波动序列,这样相对“漂亮”(美丽值的绝对值较小)的妹子可以与她旁边的两个美丽值的绝对值较大的妹子形成鲜明的对比,整个序列相对将更加“美观”(不再那么无法直视)。

一个序列是波动序列仅当序列中的每个数比周围的两个数都大或都小(如果有的话)。

现在czy希望知道,长度为N的波动序列有多少种。两种序列A和B不同当且仅当存在一个i,使得Ai≠Bi。由于这个数目可能很大,你只对它除以P的余数感兴趣。

【输入格式】

输入文件czy.in仅含一行,两个正整数N, P。

【输出格式】

输出文件czy.out仅含一行,一个非负整数,表示你所求的答案对P取余之后的结果。

【样例输入输出】

czy.in

4 7

czy.out

3

说明:共有10种可能的序列,它们是:  1324 1423 2143 2314 2413  3142 3241 3412 4132 4231

(忽略负号)

【数据规模和约定】

对于20%的数据,满足N≤10;

对于40%的数据,满足N≤18;

对于70%的数据,满足N≤550;

对于100%的数据,满足3≤N≤4200,P≤10^9。

/*20分 暴力*/
#include<iostream>
#include<cstdio>
using namespace std;
int n,p,ans;
bool vis[];
void dfs(int pre,int now,bool rel){
if(now==n+){
ans++;
if(ans>=p)ans-=p;
return;
}
for(int i=;i<=n;i++){
if(!vis[i]){
if(rel==){
if(i<pre){
vis[i]=;
dfs(i,now+,!rel);
vis[i]=;
}
}
if(rel==){
if(i>pre){
vis[i]=;
dfs(i,now+,!rel);
vis[i]=;
}
}
}
}
}
int main(){
freopen("czy.in","r",stdin);
freopen("czy.out","w",stdout);
scanf("%d%d",&n,&p);
dfs(,,);
dfs(,,);
printf("%d",ans);
}

20分 暴力

/*
f[i][j]第一位为[1,j]且第一位下降的1~i的合法排列数,先给出结论:f[i][j] = f[i][j – 1] + f[i – 1][i – j]
首先是要加上[1,j – 1]的合法排列数,然后考虑j开头的第一位下降合法排列数
这个就是求以[1,j]开头的1~n-1的第一位上升合法排列数(这个应该可以YY一下吧..)
但是第一位上升的合法排列数我们是没有算的,但注意到第一位上升和第一位下降具有对称性
所以求以[1,j]开头的1~n-1的第一位上升合法排列数就是f[i – 1][i – j]
然后如果开[4200][4200]的int的话正好会被卡掉,所以必须滚动数组..
*/
#include<iostream>
#include<cstdio>
using namespace std;
int n,p;
int dp[][];
int main(){
scanf("%d%d",&n,&p);
int x;
dp[][]=;
for(int i=;i<=n;i++){
for(int j=;j<=i;j++){
x=i&;
dp[x][j]=dp[x][j-]+dp[x^][i-j];
dp[x][j]%=p;
}
}
printf("%d",(long long)(dp[n&][n]*)%p);
return ;
}

100分 dp

czy的后宫5(树形dp)

描述

czy要召集他的妹子,但是由于条件有限,可能每个妹子不能都去,但每个妹子都有一个美丽值,czy希望来的妹子们的美丽值总和最大(虽然……)。

czy有一个周密的电话通知网络,它其实就是一棵树,根结点为czy,他可以通知一些妹子(毕竟他不认识他的所有妹子嘛),称为他的下线(也就是儿子节点),下线们继续通知自己的下线。任何妹子都可以不去,但是任何一个妹子如果要去,则她的上线(也就是她的父亲节点)一定要去。

为了使妹子美丽值总和最大,czy想安排一下,(非强制)让一些妹子去。但是妹子数很多,人脑是难以应付的,所以他想让你用电脑解决。

输入格式

输入第一行两个整数n,m表示有n个妹子,至多只能去m个妹子。(1<=m<=n)

接下来2*n行,每两行代表一个妹子的信息(如果这个妹子没有子节点,就只有一行)。

每个妹子的第一行两个整数p,s,表示这个妹子美丽值为p,子节点个数s;(-100<=p<=100)

第二行s个整数,表示这个妹子的子节点的编号。czy的编号一定为1。

对于20%数据1<=n<=10

对于60%数据1<=n<=100

对于100%数据1<=n<=1000

输出格式

输出一个整数,表示权值的最大值。

样例输入

8 5

100 2

2 3

79 2

4 5

109 3

6 7 8

100 0

100 0

100 0

101 0

108 0

样例输出

518

/*
由于给出数据可能构成多叉树,先左儿子右兄弟转换成二叉树
dp[i][j]表示讨论到i号节点,最多选j个的最大值
树形dp,从叶子节点开始(深搜到底再执行dp方程)
此时的节点分为取和不取两种情况
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=;
int n,m,dp[maxn][maxn],a[maxn];
struct node{
int l,r;
}tr[maxn];
void Insert(int from,int to){
int p=tr[from].l;
if(tr[from].l==)tr[from].l=to;
else{
while(tr[p].r!=)p=tr[p].r;
tr[p].r=to;
}
}
void DP(int now,int sum){
if(now==||sum==){dp[now][sum]=;return;}
if(dp[now][sum]!=-)return;
DP(tr[now].r,sum);//不选now节点
dp[now][sum]=dp[tr[now].r][sum];
for(int i=;i<sum;i++){//选now节点
DP(tr[now].r,i);
DP(tr[now].l,sum--i);
dp[now][sum]=max(dp[now][sum],dp[tr[now].r][i]+dp[tr[now].l][sum--i]+a[now]);
}
}
int main(){
freopen("task.in","r",stdin);
freopen("task.out","w",stdout);
scanf("%d%d",&n,&m);
int x,y;
memset(dp,-,sizeof(dp));
for(int i=;i<=n;i++){
scanf("%d%d",&a[i],&x);
for(int j=;j<=x;j++){
scanf("%d",&y);
Insert(i,y);
}
}
DP(,m);
printf("%d",dp[][m]);
}

80分 树形dp

/*
同样是dp
先跑一边dfs,记录下各点的dfs序和各子树的大小,为了方便切换子树
dp[i][j]表示讨论到i号节点,正好选j个的最优解。
dp[i+1][j-1]+v[list[i]]是选i号节点
dp[i+sz[list[i]]][j]是不选i号节点,i+sz[list[i]]是与i为兄弟关系的另一棵子树的根节点编号
由于一共选了几个是不确定的,所以最终答案在dp[1][1...m]里面找
可以一个都不选,所以最小为0,不会出现负数的情况
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=;
int n,m,dp[maxn][maxn],sz[maxn],list[maxn],a[maxn][maxn],s[maxn],v[maxn];
int cnt;
void dfs(int x){
list[++cnt]=x;
sz[x]=;
for(int i=;i<=sz[x];i++){
if(a[x][i])dfs(a[x][i]);
sz[x]+=sz[a[x][i]];
}
}
int main(){
//freopen("Cola.txt","r",stdin);
freopen("task.in","r",stdin);
freopen("task.out","w",stdout);
memset(dp,,sizeof(dp));
scanf("%d%d",&n,&m);
int x;
for(int i=;i<=n;++i){
scanf("%d%d",&v[i],&s[i]);
for(int j=;j<=s[i];++j){
scanf("%d",&x);
a[i][j]=x;
}
}
dfs();
for(int i=;i<=n;i++){
dp[i][]=;
dp[i][]=v[list[i]];
}
for(int i=n-;i>=;i--){
for(int j=;j<=m;j++){
dp[i][j]=max(dp[i+][j-]+v[list[i]],dp[i+sz[list[i]]][j]);
}
}
int ans=;
for(int i=;i<=m;i++)ans=max(ans,dp[][i]);
printf("%d",ans);
}

100分 dfs序优化dp

czy的后宫6

题目描述

众所周知的是丧尸czy有很多妹子(虽然很多但是质量不容乐观QAQ),今天czy把n个妹子排成一行来检阅。但是czy的妹子的质量实在……所以czy看不下去了。检阅了第i个妹子会增加czy a[i]的肾虚值,他打算在检阅过程中最多休息m次(一开始检阅算0次休息,就是说czy最多可以检阅m+1次),每次休息过后czy又会龙精虎猛的继续检阅。问怎样分配才能使得czy在检阅过程中的最大肾虚值最小。

当然这么简单的问题czy早就会做啦……他原来还想算算满足肾虚值最小的条件下有几种方案,但是他太虚了,所以这个问题也交给你啦。你只要输出方案数mod 32123的值即可。

输入格式

第一行输入两个正整数n、m,表示czy的妹子数、最多的休息次数

接下来2到n+1行每行输入一个数a[i],意义见上

输出格式

第一行输出一个数s,表示最小的肾虚值

第二行输出一个数t,表示方案数

样例输入

4 2

3

4

5

2

样例输出

7

3

样例解释

最小的肾虚值为7

分法有3种:34|5|2,34|52,3|4|52

‘|’表示休息

数据范围

有30%的数据,1<=n<=20

另30%的数据,1<=n<=200

另30%的数据,1<=n<=5000,1<=m<=min(n-1,1000),1<=a[i]<=1000

另10%的数据,1<=n<=20000,1<=m<=1000,a[i]只有1、2

保证80%数据随机生成,在计算过程中不会爆int

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=;
int n,m,a[maxn],ans,Ans;
bool check(int x){
int sum=,cnt=;
for(int i=;i<=n;i++){
if(sum+a[i]>x){
cnt++;
sum=a[i];
}
else sum+=a[i];
if(cnt>m)return ;
}
return ;
}
bool OK(int now,int sum){
for(int i=now;i<=n;i++)sum+=a[i];
if(sum>ans)return ;
return ;
}
void dfs(int now,int sum,int cnt){
if(cnt>m)return;
if(now==n){
if(sum+a[n]>ans&&cnt<m)Ans++;
if(sum+a[n]<=ans){
Ans++;
if(cnt<m)Ans++;
}
if(Ans>=)Ans-=;
return;
}
if(sum+a[now]>ans){
dfs(now+,a[now],cnt+);
return;
}
if(cnt==m){
if(!OK(now,sum))return;
else {
Ans++;
if(Ans>=)Ans-=;
return;
}
}
if(sum+a[now]<=ans){
dfs(now+,sum+a[now],cnt);
dfs(now+,a[now],cnt+);
}
}
int main(){
//freopen("Cola.txt","r",stdin);
freopen("visit.in","r",stdin);
freopen("visit.out","w",stdout);
int l=,r=;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]),r+=a[i];
while(l<=r){
int mid=(l+r)>>;
if(check(mid))
ans=mid,r=mid-;
else l=mid+;
}
printf("%d\n",ans);
dfs(,a[],);
if(Ans>=)Ans-=;
printf("%d",Ans);
}

30分 二分答案+暴力

蒟蒻czy又被D飞了

【题目描述】

机房里的各路巨神天天D蒟蒻CZY,早已是他们的日常任务了。(今天的机房也是很和平呢)

这一次他们安排好了一种方式来D蒟蒻Czy。每个人每次都能把Czy D飞一个高度(让Czy的高度+a[i]),由于他们的精♂力有限(尤其是某些后宫王),要保留体力应对接下来的战♂斗,所以他们每个人只会D Czy k[i]次。由于他们每个人的D人能力不同,各有所长,所以他们每个人都在Czy到一定高度h[i]以后良心发现,任由Czy自生自灭,回去玩达尔文进化岛了。(神犇们:计划通

所以Czy想知道他的速度是否能达到第二宇宙速度,离开这个可怕的地方。但是Czy太弱了,所以这个问题就交给了未来集训队的你。但是你这么吊,哪里屑解答Czy蒟蒻的问题。于是你打算只告诉他最高会飞到什么高度,让他自己算自己的速度去。(蒟蒻无人权)

【输入格式】 fly.in

第1行:1个整数N(1<=N<=100)N表示机房里有多少人今天要D 蒟蒻Czy

接下里N行,每行描述一个神犇的信息  a[i] h[i] k[i]

【输出格式】 fly.out

第1行:1个整数H,表示Czy最高会被神犇们D到哪里去

【样例输入】

7

8 35 1

5 35 1

15 35 1

8 35 1

10 35 1

4 35 1

2 35 1

【样例输出】

35

数据范围

对于40%的数据  所有人的k[i]==1, n<=10,h[i]<=100 且所有人的h[i]相等(天地良心,不要不信

对于100%的数据  1<=k[i]<=30,1<=h[i]<=10000,  1<=a[i]<=200

不做这题的下场如下

05-19 09:34