您的位置:首页 > 其它

20171009模拟赛总结

2017-10-10 10:25 225 查看
来源:不知道

分数:100+75+60

rank:10

T1 异或值

题意:

给定一个长度为n的序列,然后现在要在中间取出一个区间,求最大值^次大值的最大值

n<=2*10^6

分析:

一开始完全在分析异或有什么特点可以用在最大值次大值上的…一开始的想法是Trie树不过越往下想觉得第一题怎么可能那么麻烦。

然后就来看区间内最大值和次大值有什么特点了。

其实关于这类问题做得应该不算少了。大概有两种方法。

分治

solve(l,r)表示解决区间[l,r]内最大值最小值的问题。

首先扫一遍找到最大值,假设位置位于m,值为Mx。

那么向左向右各扫一遍,求出Mx为最大值的时候的次大值然后顺便更新一下答案。

然后solve(l,m-1); solve(m+1,r);

struct AAA{
int res;
void solve(int l,int r){
int i,mid=l,Mx,mx1;
for (i=l; i<=r; i++)if (a[i]>a[mid])mid=i;

if (mid-1>l)solve(l,mid-1);
if (mid+1<r)solve(mid+1,r);
Mx=a[mid];
mx1=a[mid-1]; res=max(res,Mx^mx1);
for (i=mid-1; i>=l; i--)if (a[i]>mx1){
mx1=a[i];
res=max(res,Mx^mx1);
}
mx1=a[mid+1]; res=max(res,Mx^mx1);
for (i=mid+1; i<=r; i++)if (a[i]>mx1){
mx1=a[i];
res=max(res,Mx^mx1);
}
}
void sol(){
solve(1,n);
printf("%d\n",res);
}
}pianfen;


单调栈

字面意思直接把每一对最大值和次大值整出来就好了

int main(){
read(n);
int i;
for (i=1; i<=n; i++)read(a[i]);
int top=0;
for (i=1; i<=n; i++){
for (; top&&a[i]>st[top]; top--)res=max(res,a[i]^st[top]);
if (top)res=max(res,a[i]^st[top]);
st[++top]=a[i];
}
top=0;
for (i=n; i>=1; i--){
for (; top&&a[i]>st[top]; top--)res=max(res,a[i]^st[top]);
if (top)res=max(res,a[i]^st[top]);
st[++top]=a[i];
}
printf("%d",res);
return 0;
}


反思

没有想到单调栈其实是很可惜的,因为写分治的时候就觉得万一出数据卡一卡就会被打回O(n^2)了然后整场比赛都挺担心第一题。

好在没有特地出数据卡。



T2 原子的裂变

题意

用一个n位数来表示一个原子,每一位的数字大小∈[0,n],可以拥有前导0但是不能全是0。

现在一个长度为n的数,裂变后第一位表示这个数有几个1,第二位表示有几个2,依次。

例如:1104

裂变后就变成2001 (2个1,0个2,0个3,1个4)

有T组数据。对于每一组数据

读入一个n位数,求有多少个n位数可以裂变成它。

n<=9;T=100。

分析

p50

首先是暴力。比如对于now(一个n位数)来说。

cnt[i]表示第i位的数字是多大。

能变成他的必然满足有cnt[i]个i。然后就变成求一个排列。

写的是BFS表示方案,DFS求排列。

//dfs
void dfs(string s,int last,int sum){
if (sum>last)return;
if (last==0){if (!mp[s]){mp[s]=1;q.push(s);}return;}
int i;
if (last>sum){
s1=s;
s1+='0';
dfs(s1,last-1,sum);
}
for (i=1; i<=len; i++)if (cnt[i]){
s1=s;
s1+='0'+i;
cnt[i]--;
dfs(s1,last-1,sum-1);
cnt[i]++;
}
}

//bfs
void bfs(){
for (; !q.empty(); res++){
sum=0;
now=q.front(); q.pop();
for (i=0; i<len; i++)cnt[i+1]=now[i]-'0',sum+=cnt[i+1];
if (sum>len)continue;
dfs("",len,sum);
}
}


p100

可以把所有数分成两类。

第一类是sum[cnt[i]]>n的,就当它为B类数,这类数字不能由其他的裂变而来。res=1

第二类是sum[cnt[i]]<=n的,就当它为A类数。res=所有可以一步变成它的res之和。

可以发现一个数的裂变过程中是不会产生环的。

除了1后面跟(n-1)个0的情况下会有一个自环,判掉就好了。

对于一个A类数,如果有一个B类数能转化成它,那么能转化成它的就全都是B类数。这里求个排列数就好了。

如果不是,那就强行求排列再往下递归就好了。

来自某同学的友情提供:即使是n=9的情况下,A类数的个数大约在40000左右。

#include<bits/stdc++.h>
using namespace std;
int n;
int jiecheng[10];
int solve(int x){
int a[10],cnt[10];
memset(a,0,sizeof(a));
memset(cnt,0,sizeof(cnt));
int k=0,l=0,tmp,i,j,res=0,t=x;
for (i=n; i>=1; i--){tmp=t%10;cnt[i]=tmp;k+=tmp;l+=tmp*i;t/=10;}
if (k>n)return 1;
if (l>n){
res=jiecheng
;
for (i=1; i<=n; i++)res/=jiecheng[cnt[i]];
return res/jiecheng[n-k]+1;
}
int id=n-k;
for (i=1; i<=n; i++){
for (j=1; j<=cnt[i]; j++)a[id++]=i;
}
int s;s=0;
for (i=0; i<n; i++)s=s*10+a[i];
if (s!=x)res+=solve(s);

for (; next_permutation(a,a
d16f
+n);){
s=0;
for (i=0; i<n; i++)s=s*10+a[i];
if (s!=x)res+=solve(s);
}
return res+1;
}

char s[15];

int main(){
int t,l,i;
scanf("%d",&t); jiecheng[0]=1;
for (i=1; i<=9; i++)jiecheng[i]=jiecheng[i-1]*i;
for (l=1; l<=t; l++){
scanf("%s",s); n=strlen(s); int x=0;
for (i=0; i<n; i++)x=x*10+(s[i]-'0');
printf("Case #%d: %d\n",l,solve(x));
}
return 0;
}


反思

其实这道题还是挺满意的?水到了意料之外的75,一开始还以为只有50的,看来数据没有出长度全是最大值还都是100…0的情况。

T3 巧克力

题意

有n个旅游团。每个团都有a[i]个人。巧克力一盒有p块。吃完前不能开新的一盒。如果一个团内所有的人吃的都是新开的巧克力的话,那他们就会很开心。

求怎么分配能使开心的旅游团数量最多。

p<=4,n<=10000,a[i]<=1e9

分析

p好小Σ

p==2 所有偶数的先吃

然后奇数一人一个

p==3

所有三的倍数先吃

然后偶数一个奇数一个

最后奇数一个一个一个

cnt3+min(cnt1,cnt2)+(max(cnt1,cnt2)-min(cnt1,cnt2)+2)/3;

p==4

怎么觉得这题不算很难…?是我想错了莫?

总之4倍数的先吃。

好 吃完了。

然后剩下余数为1,2,3的

尽可能多把它们组成4的倍数。

首先22内销,然后13搞。

搞完再1111,3333,1133,1333,1113(就是1,3混起来4个)

然后最后112,332(两个1或3,加上一个2)

如果还有剩余就再res++

#include<cstdio>
#include<iostream>
#define M 10005
using namespace std;

void read(int &x){
x=0; char c=getchar();
for (; c<'0'; c=getchar());
for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0');
}
int n,a[M];

struct AAA{
void solve(){
int i;
for (i=1; i<=n; i++)read(a[i]);
printf("%d\n",n);
}
}pianfen;

struct CCC{
void solve(){
int i,res=0,cnt=0;
for (i=1; i<=n; i++){
read(a[i]);
if (a[i]&1)cnt++;
else res++;
}
printf("%d\n",res+(cnt+1)/2);
}
}p30;

struct ACC{
void solve(){
int i,res=0,cnt1=0,cnt2=0;
for (i=1; i<=n; i++){
read(a[i]);
if (a[i]%3==0)res++;
if (a[i]%3==1)cnt1++;
if (a[i]%3==2)cnt2++;
}
printf("%d\n",res+min(cnt1,cnt2)+(max(cnt1,cnt2)-min(cnt1,cnt2)+2)/3);
}
}p60;

struct AAC{
void solve(){
int i,res=0,cnt1=0,cnt2=0,cnt3=0;
for (i=1; i<=n; i++){
read(a[i]);
if ((a[i]&3)==0)res++;
if ((a[i]&3)==1)cnt1++;
if ((a[i]&3)==2)cnt2++;
if ((a[i]&3)==3)cnt3++;
}
int num=min(cnt1,cnt3);
res+=num; cnt1-=num; cnt3-=num;
res+=cnt2/2; cnt2=cnt2&1;
int s=(cnt1+cnt3);
num=s/4;
res+=num; s&=3;
if (s>=2&&cnt2) res++,s-=2,cnt2--;
if (s||cnt2) res++;
printf("%d\n",res);
}
}p100;

int main(){
//  freopen("chocolate.in","r",stdin);
//  freopen("chocolate.out","w",stdout);
int t,l,p;
read(t);
for (l=1; l<=t; l++){
printf("Case #%d: ",l);
read(n); read(p);
if (p==1)pianfen.solve();
if (p==2)p30.solve();
if (p==3)p60.solve();
if (p==4)p100.solve();
}
return 0;
}


反思

比赛的时候只敲到60,因为p==4的情况似乎弄错了一点结果一分都没有。

有点可惜不过还算很满意了。(因为题目好像不是很难)



总结

其实。很满意啦——没了。

非要说的话这几天也在写深搜,像第二题这种把状态分类来求的做法,还是挺有用的。能有效的减掉一层枝。

然后第三题的话没水到所有分有点可惜不过也在意料之内。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  总结