来源:不知道
分数: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[n];
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+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的情况似乎弄错了一点结果一分都没有。
有点可惜不过还算很满意了。(因为题目好像不是很难)
总结
其实。很满意啦——没了。
非要说的话这几天也在写深搜,像第二题这种把状态分类来求的做法,还是挺有用的。能有效的减掉一层枝。
然后第三题的话没水到所有分有点可惜不过也在意料之内。