A. 大家一起点外卖
大数据
排序之后会超时, 需要使用基数排序特别需要牢记教训
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
#include <climits>
#define MAXN 500005
#define eps 1e-5
#define MOD 1000000007
#define test
#define For(i,m,n) for(int i=(m);i<(n);i++)
#define vecfor(iter,a) for(vector<int>::iterator iter=a.begin();iter!=a.end();iter++)
#define rep(i,m,n) for(int i=(m);i<=(n);i++)
#define LL long long
/*author birdstorm*/
using namespace std;
template<class T>
inline bool read(T &n){
T x = 0, tmp = 1; char c = getchar();
while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
if(c == EOF) return false;
if(c == '-') c = getchar(), tmp = -1;
while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
n = x*tmp;
return true;
}
template <class T>
inline void write(T n) {
if(n < 0) {
putchar('-');
n = -n;
}
int len = 0,data[20];
while(n) {
data[len++] = n%10;
n /= 10;
}
if(!len) data[len++] = 0;
while(len--) putchar(data[len]+48);
}
int p[MAXN];
int aa[MAXN<<2];
int m, tmp;
int main()
{
int t, n;
read(t);
while(t--){
read(n), read(m);
memset(aa,0,sizeof(aa));
int g;
For(i,0,n) read(g), aa[g]++;
int top=0;
bool isok=false;
int m1, m2;
rep(i,0,1000000){
if(i>m-i) break;
if(i==m-i&&aa[i]>=2){
m1=i,m2=i,isok=true;
break;
}
if(i!=m-i&&aa[i]&&aa[m-i]){
m1=i, m2=m-i; isok=true;
}
}
if(!isok) printf("Sad");
else printf("%d %d",m1,m2);
puts("");
}
return 0;
}
B. 田田的公司
十分裸的并查集, 需要注意在合并操作时, 如果两个公司已经在同一联盟下, 不能更新答案.
C. 崔逗逗的难题
纯数学. 先计算中心区域, 可以通过菱形+四个弓形得到
在计算复杂图形是要想办法将复杂图划分为简单图: 四边形, 三角形, 圆, 弓形, 扇形, 等
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define For(i,m,n) for(int i=(m);i<(n);i++)
/*author birdstorm*/
using namespace std;
const double pi=acos(-1.0);
int main()
{
double n;
while(~scanf("%lf",&n)){
double g=2-sqrt(3);
double c=(pi-2.0)*n*n, b;
double a=n*n*g+n*n*(pi/3.0-1);
c=c-a*2.0; b=n*n-a-c;
printf("%f %f %f\n",a,c,b);
}
return 0;
}
D. 崔逗逗给你信心
#打表, 数学#
十分可惜的一题, 做出来了之后提交手速慢了几秒钟, 比赛已经结束了
首先是打表观察
符合条件的数似乎没有任何联系
但是用二进制打表之后, 可以明显发现所有数转成二进制后都不会出现相邻1. 仔细观察后发现所有没有出现相邻1的二进制数都是一个解
接下来可以证明: (x*3)异或(x*2)异或(x)的值为0 的充要条件为x为一个无相邻二进制1的数
注意到乘以2的操作其实就是将二进制左移了一位, 这样3*x的答案其实就是2*x+x, 也就是二进制左移一位后相加.
也就是说: (2*x)异或(x) 必然是(3*x) 充分性即证.
接下来证明必要性:
反证设有 x 含有相邻二进制1, 且(x)异或(x*2)=x*3
则 x+2*x的值必然会在相邻1处发生进位为0的情况, 和原假设不符
必要性证毕
接下来就是计算第n个符合条件的值
可以发现所有的符合条件的数(不包括0和1)都会包含一个后缀子串, 使得这个子串符合条件.
比如
100001001
包含 1001
对这部分进行计算, 可以发现每一个二进制1都代表了斐波那契数列的前缀和
我们可以很简单的进行预处理, 然后构造出最小的符合条件的二进制序列
构造方法如下: 从二进制序列头部开始扫描, 直到发现相邻1, 然后将后一个1变为0, 之后的串变为101010...10的序列
贪心方法的正确性显然.
所以我们可以写出代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define MAXN 100005
#define MOD 1000000009
#define For(i,m,n) for(int i=(m);i<(n);i++)
#define LL long long
/*author birdstorm*/
using namespace std;
LL dp[505], sum[505];
void change(int *s, int w)
{
for(int i=w-2;i>=0;i--){
if(s[i]==1&&s[i+1]==1){
s[i]=0;
for(int j=i-1;j>=0;j--){
if(j%2==i%2) s[j]=0;
else s[j]=1;
}
}
}
}
int main()
{
LL n;
int c[100];
dp[0]=0; dp[1]=1; sum[0]=1; sum[1]=1;
For(i,2,500) dp[i]=(dp[i-1]+dp[i-2])%MOD, sum[i]=(sum[i-1]+dp[i])%MOD, sum[i-1]=(sum[i-1]+1)%MOD;
while(~scanf("%lld",&n)){
LL ans=1;
int ccnt=0;
while(n){
c[ccnt]=(n&1);
ccnt++; n>>=1;
}
change(c,ccnt);
For(i,0,ccnt){
if(c[i]) ans=(ans+sum[i])%MOD;
}
printf("%lld\n",ans);
}
return 0;
}
E. 焦级长搭积木
dp, dp公式容易很想出来,
dp[m][h][n]=dp[m-1][h-1][n-m]+dp[m+1][h-1][n-m] 保存在m,h,n状态下的方案数
三维代表起点, 高度和总和
每次m,h,n查询前做一次记忆化搜索 复杂度为O(6*2^10)
接下来对于第k个字典序的值, 通过每次都判断/减去对应的方案数 即可O(h) 解决
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
#include <climits>
#define MAXN 100005
#define eps 1e-5
#define MOD 1000000009
#define test
#define For(i,m,n) for(int i=(m);i<(n);i++)
#define vecfor(iter,a) for(vector<int>::iterator iter=a.begin();iter!=a.end();iter++)
#define rep(i,m,n) for(int i=(m);i<=(n);i++)
#define LL long long
/*author birdstorm*/
using namespace std;
LL dp[11][61][580];
LL dfs(int first,int length, int SUM)
{
LL &ret=dp[first][length][SUM];
if(ret!=-1) return ret;
if(SUM<=0) return ret=0;
ret=0;
if(length==1){
if(SUM==first) return ret=1;
else return ret=0;
}
if(first!=10&&SUM>first) ret+=dfs(first+1,length-1,SUM-first);
if(first!=1&&SUM>first) ret+=dfs(first-1,length-1,SUM-first);
return ret;
}
int sta[100], top;
bool print(int first,int length,int sum,LL k)
{
int i;
if(k==0&&length==1&&sum==first){
sta[top++]=first;
return true;
}
if(sum<=0||length==0) return false;
//printf("(sum=%d)\n",sum);
if(first!=1&&k<dp[first-1][length-1][sum-first]) {
if(print(first-1,length-1,sum-first,k)){
sta[top++]=first;
return true;
}
}
if(first==1){
if(print(2,length-1,sum-1,k)){
sta[top++]=first;
return true;
}
}
if(first!=10&&k>=dp[first-1][length-1][sum-first]){
k-=dp[first-1][length-1][sum-first];
if(print(first+1,length-1,sum-first,k)){
sta[top++]=first;
return true;
}
k+=dp[first-1][length-1][sum-first];
}
if(first==10){
if(print(9,length-1,sum-10,k)){
sta[top++]=first;
return true;
}
}
return false;
}
int main()
{
int n, h, m;
LL k;
memset(dp,-1,sizeof dp);
while(~scanf("%d%d%d",&n,&h,&m)){
top=0;
dfs(m,h,n); printf("%lld\n",dp[m][h][n]);
while(scanf("%lld",&k),k!=-1){
top=0; k--;
int tm=m;
if(m!=1&&k<dp[m-1][h-1][n-m]) tm=m-1;
else if(m!=10) k-=dp[m-1][h-1][n-m], tm=m+1;
if(m==10) tm=9;
if(m==1) tm=2;
printf("%d",m);
print(tm,h-1,n-m,k);
for(int i=top-1; i>=0; i--) printf(" %d",sta[i]);
putchar('\n');
}
}
return 0;
}