==周一到周五每天比赛啊啊啊。。晚上回来就剩下玩了题都不想看的。。
不过吧,多校赛被一群(还是好几群)神奇的中学生狂虐。。今天更是被中学生出的题难到根本无从下手。。于是怒睡了一下午0.0
【whn犇感叹:高三是人生知识最丰富的时候啊】
不过下午睡了这样多的觉,晚上就睡不着了。。来把磨蹭了好久的题解发了吧+-+
A. 大家一起点外卖 2014新生暑假个人排位赛04
题目描述
大家都回了本部,本部生活虽然没有宏福天(mo)堂(gui)般的生活,但是却有很多外卖,今天有一家饭店新开张,凡是两个人的餐点费用之和刚好为m可以免运费,在今天一起点外卖且免运送费的两个人,各自点的外卖价格差值最小的有大惊喜。在机房的n个同学今天打算一起点外卖,但是每个人都只想要点某一个价格x的餐点,请你帮忙规划一下大家应当如何组合,并给出最有可能获得今天大惊喜的价格组合。
输入格式
第一行输入样例数T 对于每一个样例 第一行输入整数n(0≤ n≤500000),整数m(0≤m≤2000000)接下来的n行,每行输入一个整数x代表每一个人想点的餐点的价格 0≤x≤1000000
输出格式
如果能够找到最有可能获得今天大惊喜的价格组合,则输出两个人所点的餐点的价格,小的价格在前。
如果所有人都没办法免运送费,则输出“Sad”
输入样例
2
2 3
1
3
4 3
1
2
3
4
输出样例
Sad
1 2
开始我是用一个multiset【再次提醒自己set和multiset的区别!!卡在这上好多次了】存的出现过的价格,被卡Onlogn了。。还得是On的能过
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define maxn 500005
#define inf 2000005
using namespace std;
int t,n,m,Id1,Id2,Mm;
int a[maxn];
int Hashs[inf];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(Hashs,0,sizeof(Hashs));
for(int i=1;i<=n;i++)
{scanf("%d",&a[i]);Hashs[a[i] ]++;}
Mm=inf;
for(int i=1;i<=n;i++)
if(a[i]*2==m)
{
if(Hashs[a[i] ]>=2) {Id1=a[i];Id2=a[i];Mm=0;break;}
}
else
{
if(a[i]>=0 && a[i]<=m && Hashs[m-a[i]]&& Mm>abs(m-a[i] -a[i])) {Id1=m-a[i];Id2=a[i];Mm=abs(m-a[i] -a[i]);}
}
if(Mm==inf)printf("Sad\n");
else printf("%d %d\n",min(Id1,Id2),max(Id1,Id2));
}
return 0;
}
-----------------------------------------------------------------------------------------------------------------------
B. 田田的公司 2014新生暑假个人排位赛04
题目描述
田田家开了一家大公司,面对现在的形势,很多公司选择了联盟。联盟一旦成立就意味着联盟内的每个公司互相之间都认可结盟。所以当决定以一家公司为对手的时候就一定要计算出这家公司所在联盟的总实力值来决定有没有能力击败对手。
现在有n个公司,和m条信息。每条信息可能是某两个公司宣布联盟或者查询某个公司所在联盟的实力值
输入格式
多组数据
第一行为数据量T,每个case第一输入n,m ,之后一行输入n个数ai表示第i个公司的实力值,之后m行输入信息,有两种信息
第一种为两个公司联盟,该行为1 x y ,1表示联盟操作,即x,y两个公司联盟,
第二种为查询某个公司,该行为2 x,2表示查询操作,即查询x公司所在联盟的实力值;
T<=10
n,m<=10^5
ai<=10^6
输出格式
每个查询一行输出,输出那个公司联盟的实力值
输入样例
1
5 5
1 2 3 4 5
1 1 2
2 2
2 5
1 2 3
2 1
输出样例
3
5
6
最近自己手写过两次并查集,都是改了好久才磕磕绊绊的过
维护和的时候,只需要在Union时候转移下和,FindSet时候不用传递呀,要不就加重复了~~~
还有,合并的时候要判断一下这两点是否之前就在一个集合中,否则就会重复加一个集合里的和值
并查集这个东西,经常要附带着维护一个数组,怎么保证维护的过程少些bug呢。。
没有什么模板。。就是得捣明白并查集是怎么工作的
总的来说,注意,FindSet时要维护什么,递归乱了的话,就声明一个zuzong变量(wzt菊苣教我的)
还有union时候维护什么,是否需要判断之前是否在一个集合中
今天的神奇中学生教育我们说,并查集其实有各种神奇的用处的
还是上ac代码吧
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 100005
using namespace std;
int fa[maxn];
long long tot[maxn];
int t,n,m;
int FindSet(int x)
{
if(fa[x]==x) return x;
return fa[x]=FindSet(fa[x]);
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {scanf("%lld",&tot[i]);fa[i]=i;}
for(int i=1;i<=m;i++)
{
int Id;
scanf("%d",&Id);
if(Id==1)
{
int x,y;
scanf("%d%d",&x,&y);
int rootx=FindSet(x);
int rooty=FindSet(y);
if(rootx!=rooty)
{
fa[rooty]=rootx;
tot[rootx]+=tot[rooty];
}
}
else
{
int x;
scanf("%d",&x);
printf("%lld\n",tot[FindSet(x)]);
}
}
}
return 0;
}
-----------------------------------------------------------------------------------------------------------------------
439. 崔逗逗的难题
题目描述
崔逗逗放假回家后每天只能吃饭睡觉打豆豆感觉好无聊,该怎么给自己找找乐子呢?于是他主动去帮邻家初中小妹妹做作业。他看到了这样一道题:在一个边长为a(0≤a≤10001)的正方形ABCD中,分别以A、B、C、D为圆心做半径为a的四条弧,如图。求标号为1,2,3的图形面积。崔逗逗想了好久居然不会做,但是做不出来又会在小妹妹面前很没面子,所以他找到你来帮忙。
输入格式
多组数据。每组数据包含一个实数a,表示正方形的边长。
输出格式
对于每组数据,输出标号为1,2,3的图形面积。保留6位小数。
输入样例
0.2
输出样例
0.012606 0.020452 0.006942
开始以为是巨水的题,交wa之后发现这题其实很卡精度的
double型能保证整数加小数部分一共14位左右是精确地,这题整数8位小数6位,再多点常数就超了
所以应该用long double
当时不知道这个神奇的东西,怎么办了呢,尽量减少中间计算过程,直接一步输出结果也能提高精度,所以还是double水过啦~
代码巨短
#include <iostream>
#include <cstdio>
#include <cmath>
#define PI (4*atan(1))
using namespace std;
long double a;
int main()
{
while(~scanf("%Lf",&a))
{
long double ans1=a*a*(PI/3-sqrt(3)+1);
long double ans2=a*a*4*(PI/12+sqrt(3)/2-1);
long double ans3=a*a-ans1-ans2;
printf("%.6Lf %.6Lf %.6Lf\n",ans1,ans2,ans3);
}
return 0;
}
-----------------------------------------------------------------------
D. 崔逗逗给你信心 2014新生暑假个人排位赛04
题目描述
崔逗逗是一个善良的学长,最喜欢为学弟学妹出水题。他给大家一个整数n,求0<=x<=n的范围内,有多少个数满足(x)^(2x)^(3x)==0 (^是异或符号)。你只要告诉他对1000000009取余的答案就好啦。
输入格式
输入有多组数据,数量在100以内,每行一个整数n(0<=n<=10^18)。
输出格式
每组数据输出一行答案。
输入样例
1
2
输出样例
2
3
由打表及观察得,满足(x)^(2x)^(3x)==0的数,二进制中一定没有两个1是挨着的
于是就转化成找比n小的满足以上条件的排列有多少种
(最近做了好多排列组合求方案数的题。。基本这种题都是用递推做的,偶尔打表观察一下,能简化不少运算。总之,以前是很虚这种数学题的,现在好多了~)
不过这题找比n小的排列,这一个地方真是比较难写,我场上交的wa其实离ac只有一行代码,两个1挨着的时候我直接break了没把后面的1所带的排列数加上。。
有神犇打表找出了Fibonacci。。(就是我solve算出的东西)顿时感觉少了很多代码量。。其实这不是什么关键规律。。
数位问题嘛。。还是一步一步从高位到低位推吧/-\(场上思维有点混乱,结果思维出了点bug)
不好叙述,直接上代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define mo 1000000009
using namespace std;
long long input;
long long ans;
int Index;
int a[100];
long long C[100][100];
void GetC()
{
for(int i=0;i<=99;i++) {C[i][0]=1;C[i][i]=1;}
for(int i=1;i<=99;i++)
for(int j=1;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
}
long long Solve(int x)
{
long long he=0;
for(int i=0;x+1-i>=i;i++)
he=(he+C[x+1-i][i])%mo;
return he;
}
int main()
{
GetC();
while(~scanf("%lld",&input))
{
Index=0;
while(input!=0)
{
Index++;
a[Index]=input%2;
input=input/2;
}
ans=0;
for(int i=Index;i>=1;i--)
{
if(i<Index && a[i]==1 && a[i+1]==1) {ans=(ans+Solve(i-1) )%mo;break;}
if(a[i]==1) ans=(ans+Solve(i-1) )%mo;
}
bool flag=true;
for(int i=2;i<=Index;i++)
if(a[i]==1 && a[i-1]==1) {flag=false;break;}
if(flag) ans=(ans+1)%mo;
printf("%lld\n",ans);
}
return 0;
}
-------------------------------------------------------------------------------
434. 焦级长搭积木
题目描述
焦级长特别喜欢搭积木,一天他创造了一种新的玩法。焦级长一共有N个积木,从下往上一共搭了H层,其中最底层有M个积木,除最底层,每一层的积木数是它下一层积木数+1或-1且每层积木不超过10个。
输入格式
input 输入含多组数据。每组第一行为三个整数N,H,M,第二行后每行一个整数K,以-1结束(1<=N<=540,H<=60,M<=10,K<=10^10)。
输出格式
output 第一行是满足N、H、M的积木搭建方案总数,以后每一行对于对应的K,给出顺序排列的第K种方案(最小的排列为第一种)。 如样例中,2 1 2 3 2 3是一种方案,代表一层的积木数从下往上分别为212323,232321也是一种方案,212323比232321要小,即第一个数小的排前面,第一个数相等的就看第二个数,以此类推。 这里的K就是求第K个按顺序排列的方案。
输入样例
13 6 2
1
3
-1
输出样例
3
2 1 2 3 2 3
2 3 2 3 2 1
这题一看到字典序第几位的,顿时就虚了。。场上没敢a。。
后来s(adnim神犇)说这题不怎么难,按照dp想就行,于是我就试着想了想,果然做出来啦~~~
(信心其实很重要)
dp[i][j][k]表示,还剩i块积木,搭到第j层了,这层用了k个积木
字典序那个怎么处理呢,因为每层的值都是由上层的两个数加过来的,所以我们[还原序列]时,判断是从上半区+1还是下半区-1来的即可,从高往低位判断就能保证顺序是字典序了
#include <iostream>
#include <cstdio>
#include <cstring>
int n,h,m,len,pre,remain;
long long k;
long long dp[550][70][12];
int main()
{
while(~scanf("%d%d%d",&n,&h,&m))//2 2 1
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=10;i++) if(n>=i) dp[n-i][h][i]=1;
for(int i=h-1;i>=1;i--)
for(int j=1;j<=10;j++)
for(int k=n;k>=0;k--)
if(k+j<=n)
dp[k][i][j]=dp[k+j][i+1][j-1]+dp[k+j][i+1][j+1];
printf("%lld\n",dp[0][1][m]);
while(~scanf("%lld",&k))
{
if(k==-1) break;
printf("%d",m);
len=2;pre=m;remain=m;
while(len<=h)
{
if(pre>1 && k<=dp[remain][len][pre-1])
{
printf(" %d",pre-1);
pre=pre-1;
remain+=pre;
len++;
}
else
if(pre<10)
{
printf(" %d",pre+1);
k-=dp[remain][len][pre-1];
pre=pre+1;
remain+=pre;
len++;
}
}
printf("\n");
}
}
return 0;
}
===========================================EOF========================================