[置顶] 2013腾讯编程马拉松初赛第3场(3月23)(HDU 4515 HDU4516 HDU4517 HDU4517 HDU4519)
第一题:小Q系列故事——世界上最遥远的距离
链接 :http://acm.hdu.edu.cn/showproblem.php?pid=4515
题解:由于水题,直接贴roro大牛的代码
#include <stdio.h> int day[2][13] = {0,31,28,31,30,31,30,31,31,30,31,30,31,0,31,29,31,30,31,30,31,31,30,31,30,31}; int judge(int y) { if (y%4 == 0 && y % 100 != 0 || y % 400 == 0) return 1; else return 0; } int main() { int t, n; scanf("%d",&t); while (t--) { int sy = 2013, sm = 3, sd = 24; scanf("%d",&n); sd += n; while (1) { int y = judge(sy); if(sd > day[y][sm]) { sd -= day[y][sm]; sm += 1; if(sm > 12) { sm = 1; sy += 1; } } else break; } printf("%4d/%02d/%02d ",sy,sm,sd); sy = 2013, sm = 3, sd = 24; sd -= n; while (1) { if(sd < 1) { sm -= 1; if(sm < 1) { sm = 12; sy -= 1; } int y = judge(sy); sd += day[y][sm]; } else break; } printf("%4d/%02d/%02d\n",sy,sm,sd); } }
第二题:威威猫系列故事——因式分解
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4516
题解:(转自大牛http://www.cnblogs.com/Lyush/archive/2013/03/24/2978239.html)
题意:给定一个多项式,对其进行因式分解。
解法:由于多项式每一项系数绝对值不超过1000,由于最后解的形式为(x-a)(x-b)(x-c)(x-d)(x-e)其中a*b*c*d*e一定是最后的常数项系数,因此a, b, c, d, e的取值范围都在[-1000, 1000]内,因此枚举所有的根,剩下的就是重根的时候该怎么办?一个解决办法就是对原多项式进行求导,如果一个根t是f(x)的K重根的话,那么t一定是f(x)'的K-1重根。该题的字符串处理我没写好,后面调了很久。还有就是由于有5次方存在,因此代入时使用long long计算。
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #include <cctype> #include <vector> using namespace std; char str[500]; int seq[10]; long long _pow(int a, int b) { long long ret = 1; for (int i = 0; i < b; ++i) { ret *= a; } return ret; } int jiechen[10] = {1, 1, 2, 6, 24, 120}; void qiudao(int *rec, int k) { for (int i = k; i <= 5; ++i) { rec[i-k] = jiechen[i] / jiechen[i-k] * seq[i]; } } bool judge(int rec[], int x) { long long sum = 0; for (int i = 0; i <= 5; ++i) { sum += 1LL * rec[i] * _pow(x, i); } return sum == 0; } void gao(char ts[]) { int len = strlen(ts); int p = -1, a, b; for (int i = 0; i < len; ++i) { if (ts[i] == 'x') { if (isdigit(ts[i-1])) { ts[i] = '\0'; } else { ts[i] = '1'; } p = -2; } else if (ts[i] == '^') { ts[i] = '\0'; p = i+1; } } a = atoi(ts); if (!a && p != -1) a = 1; if (p == -1) { b = 0; } else if (p == -2) { b = 1; }else { b = atoi(ts+p); } seq[b] += a; } void solve() { vector<int>v; int cnt = 0; memset(seq, 0, sizeof (seq)); char ts[50], *p; p = strtok(str, "+"); while (p) { strcpy(ts, p); gao(ts); p = strtok(NULL, "+"); } for (int i = 5; i >= 0; --i) { if (seq[i] != 0) { cnt = i; break; } } for (int i = -1000; i <= 1000; ++i) { for (int j = 0; j < cnt; ++j) { int rec[10] = {0}; qiudao(rec, j); if (judge(rec, i)) { v.push_back(i); } else { break; } } } //x^4-x^2 //x^4-7x^3+18x^2-20x+8 //x^3-13x^2+55x-75 //x^2+5x^2-6x^2+x^2+2x-20x+30x-10x+8-7 //x^5-10x^4+39x^3-74x^2+68x-24 //以上都是能够分解的式子 if (v.size() != cnt || seq[cnt] != 1 || cnt == 0) { printf("-1\n"); } else { sort(v.begin(), v.end()); for (int i = v.size()-1; i >= 0; --i) { if (v[i] < 0) { printf("(x+%d)", -v[i]); } else if (v[i] == 0) { printf("x"); } else { printf("(x-%d)", v[i]); } } puts(""); } } int main() { int T, ca = 0; scanf("%d", &T); while (T--) { scanf("%s", str); int len = strlen(str); for (int i = 0; i < len; ++i) { if (str[i] == '-') { for (int j = len-1; j >= i; --j) { str[j+1] = str[j]; } str[i] = '+'; len += 1; ++i; str[len] = '\0'; } } printf("Case #%d: ", ++ca); solve(); } return 0; }
第三题:小小明系列故事——游戏的烦恼
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4518
题解:(转自网上大牛:http://blog.csdn.net/zhuhuangjian/article/details/8711287)
用p[i][j]存放0~i-1 * 0~j-1里的黑点数目。
然后对枚举,
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int n,m,x,y; char map[2001][2001]; int p[2001][2001]; int main() { while(~scanf("%d%d",&n,&m)) { if(n==0&&m==0) break; else { memset(p,0,sizeof(map)); scanf("%d%d",&x,&y); int i,j; for(i=0;i<n;i++) scanf("%s",map[i]); if(map[0][0]=='*') p[0][0]=1; for(i=0;i<n;i++) for(j=0;j<m;j++) { int a,b,c; if(i-1>=0) a=p[i-1][j]; else a=0; if(j-1>=0) b=p[i][j-1]; else b=0; if(i-1>=0&&j-1>=0) c=p[i-1][j-1]; else c=0; p[i][j]=a+b-c; if(map[i][j]=='*') p[i][j]++; } int ans=0; int z=x*y; for(i=0;i+x<=n;i++) for(j=0;j+y<=m;j++) { int wo,ai,tm; if(i<=0) wo=0; else wo=p[i-1][j+y-1]; if(j<=0) ai=0; else ai=p[i+x-1][j-1]; if(i<=0||j<=0) tm=0; else tm=p[i-1][j-1]; if(p[i+x-1][j+y-1]+tm-wo-ai==z) ans++; } if(x!=y)//如果x==y,横放竖放都一样 { for(i=0;i+y<=n;i++) for(j=0;j+x<=m;j++) { int wo,ai,tm; if(i<=0) wo=0; else wo=p[i-1][j+x-1]; if(j<=0) ai=0; else ai=p[i+y-1][j-1]; if(i<=0||j<=0) tm=0; else tm=p[i-1][j-1]; if(p[i+y-1][j+x-1]+tm-wo-ai==z) ans++; } } printf("%d\n",ans); } } return 0; }
另一个大牛的思路: 做法:关键是统计每个矩形的面积,刚开始用二维树状数组来统计超时了。其实用递推的方法可以很快的预处理求出矩形(1,1)~(i,j)的面积,接下来枚举每个符合条件的矩形,判断其面积是否等于x*y即可。(http://www.cnblogs.com/fzf123/archive/2013/03/23/2977813.html)
View Code /* *Author: Zhaofa Fang *Created time: 2013-03-23-21.36 *Language: C++ */ #include <cstdio> #include <cstdlib> #include <sstream> #include <iostream> #include <cmath> #include <cstring> #include <algorithm> #include <string> #include <utility> #include <vector> #include <queue> #include <stack> #include <map> #include <set> using namespace std; typedef long long ll; #define DEBUG(x) cout<< #x << ':' << x << endl #define REP(i,n) for(int i=0;i < (n);i++) #define REPD(i,n) for(int i=(n-1);i >= 0;i--) #define FOR(i,s,t) for(int i = (s);i <= (t);i++) #define FORD(i,s,t) for(int i = (s);i >= (t);i--) #define PII pair<int,int> #define PB push_back #define MP make_pair #define ft first #define sd second #define lowbit(x) (x&(-x)) #define INF (1<<30) int n,m; int C[2011][2011]; char str[2011][2011]; void fun(){ C[0][0] = 0; FOR(i,1,n){ C[i][0] = 0; FOR(j,1,m){ C[i][j] = C[i][j-1]+(str[i][j]=='*'?1:0); } } FOR(i,2,n){ FOR(j,1,m){ C[i][j] += C[i-1][j]; } } } int Area(int x1,int y1,int x2,int y2){ if(x1>x2)swap(x1,x2); if(y1>y2)swap(y1,y2); return C[x2][y2]-C[x2][y1-1]-C[x1-1][y2]+C[x1-1][y1-1]; } int main() { //freopen("in","r",stdin); //freopen("out","w",stdout); while(~scanf("%d%d",&n,&m),n,m){ int x,y; scanf("%d%d",&x,&y); FOR(i,1,n)scanf("%s",str[i]+1); fun(); int ans = 0; FOR(i,1,n){ FOR(j,1,m){ if(str[i][j] != '*')continue; int xx1 = i+x-1; int yy1 = j+y-1; if(!(xx1<1||yy1<1||xx1>n||yy1>m)){ if(Area(i,j,xx1,yy1) == x*y)ans++; } int xx2 = i+y-1; int yy2 = j+x-1; if(xx2 == xx1 && yy2==yy1)continue; if(xx2<1||yy2<1||xx2>n||yy2>m)continue; if(Area(i,j,xx2,yy2) == x*y)ans++; } } printf("%d\n",ans); } return 0; }
第四题:吉哥系列故事——最终数
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4518
题解:(转自大牛:http://hi.baidu.com/chenwenwen0210/item/65669bc0a55e48f9ee183bbc)
解题报告:
基本思想,由于题目中很多和斐波那切有关的东西,我们要抓住斐波那切数列的增长速度是非常快这个特点来解决这个问题。
首先看到最小的最终数是13,那么对于10^11这个数据来说的话,13是一个解,解的上限是不超过(10^11-13)+10^11
算他是2*10^11好了。
那么我们只要统计一下这个区间内有哪些最终数就行了。
其实这样的最终数并不多。
我们枚举到第54个斐波那切数字的时候已经是超过10^12,就算1到2*10^11这些数字都是题目中说的F数好了,也就是54个。
我有一个初步的想法,就是把小于等于2*2^11的斐波那切数字找出来。然后对于每一个最终数,我们二分一个答案X来计算一下
小于等于X的数字里面有多少是F数。
如果刚刚好是给定的斐波那切个数的话,这个数字就是那个斐波那切数对应的最终数了。
这对于统计这一块我们还是采用数位DP来做。
由于直接计算包含斐波那切数字的个数不好计算,我们可以反过来计算,计算一下不包含斐波那切数字的那些数。然后总数目减去就行了。
我们可以把斐波那切数看成是字符串,做成AC自动机,然后通过AC自动机来数位DP。
dp[i][j]代表长度为i,在自动机中的状态为j的情况
dp[i][j]=sum{dp[i-1][k]} for each node[k].next[bit]==j
先写个思路,具体的实现明天再写吧。
看了best solution里面的人代码好短,不知道他们是怎么做的。
我这样的写法至少得3K吧。
PS:最近数位DP做得有一些多啊。
早上起来实现了一下,一次AC了,还有一种更猥锁的做法^_^,就是数字总共就55个,把所有的数字打不一个表,这样代码就很短了。
#include<stdio.h> #include<math.h> #include<string.h> typedef __int64 lld; const int MAX=1005; const int MAXSON=10; struct { int id,next[MAXSON],fail; }node[10000]; int n,tot; int q[1000000]; void clr() { int i; tot++; for(i=0;i<MAXSON;i++) node[tot].next[i]=0; node[tot].id=node[tot].fail=0; } void ins(lld n) { lld tmp,bit[55]; int i=0; while(n) { bit[i++]=n%10; n/=10; } int h=0; for(i--;i>=0;i--) { tmp=bit[i]; if(node[h].next[tmp]==0) { clr(); node[h].next[tmp]=tot; } h=node[h].next[tmp]; } node[h].id++; } void get_fail() { int h=0,i; int f=-1,r=0; int tmp,fail,k; q[0]=0; while(f!=r) { tmp=q[++f]; if(node[node[tmp].fail].id>0)//自动机添加一个往前走的东西。错在这里啊,好久没有用AC自动机了 node[tmp].id=1; for(i=0;i<MAXSON;i++) { if(node[tmp].next[i]==0) { fail=node[tmp].fail; node[tmp].next[i]=node[fail].next[i]; continue; } k=node[tmp].next[i]; fail=node[tmp].fail; if(node[fail].next[i]!=k) node[k].fail=node[fail].next[i]; else node[k].fail=0; q[++r]=k; } } } const double EPS=1.0e-8; int dblcmp(double x) { if(fabs(x)<EPS)return 0; return x<0?-1:1; } const lld INF=(lld)(1000000000)*(lld)(1000); lld dp[15][1000]={0}; lld ans[55],c; lld f[55]={1,2}; void init() { int i,j,k; for(i=0;i<=tot;i++) { if(node[i].id==0) { dp[0][i]++; } } int h=0; for(i=1;i<15;i++) { for(j=0;j<=tot;j++) { if(node[j].id>0)continue; for(k=0;k<10;k++) { h=node[j].next[k]; if(node[h].id>0)continue; dp[i][j]+=dp[i-1][h]; } } } // freopen("C:\\a.txt","w",stdout); //for(i=1;i<2;i++)for(j=0;j<=tot;j++) { printf("dp[%d][%d]=%I64d\n",i,j,dp[i][j]); } } lld calc(lld n) { if(n==0)return 0; int h,i=0,j; lld m=n-1; lld bit[55]; while(n) { bit[i++]=n%10; n/=10; } int len=i; lld ret=0; for(i=1;i<len;i++) { for(j=1;j<10;j++) { h=node[0].next[j]; if(node[h].id==0) { ret+=dp[i-1][h]; } } } h=0; for(i=len;i>0;i--) { j=0; if(i==len)j++; for(;j<bit[i-1];j++) { int th=node[h].next[j]; if(node[th].id>0)continue; ret+=dp[i-1][th]; } h=node[h].next[bit[i-1]]; if(node[h].id>0)break; } return m-ret; } lld bin(lld aim) { lld low=0,high=INF; lld mid,ret=-1; while(low<=high) { mid=(low+high)>>1; if(calc(mid)>=aim) { ret=mid-1; high=mid-1; } else low=mid+1; } return ret; } int main() { lld n; int i; tot=-1; clr(); int sum=0; for(i=2;i<55;i++) { f[i]=f[i-1]+f[i-2]; if(f[i]>=10) { ins(f[i]); sum+=log10(f[i]*1.0)+1; } else if(f[i]>=INF)break; } // printf("sum=%d\n",sum); // printf("tot=%d\n",tot); get_fail(); init(); for(i=2;i<55;i++)f[i]=f[i-1]+f[i-2]; c=0; for(i=0;i<55;i++) { // printf("f[%d]=%I64d\n",i,f[i]); ans[c]=bin(f[i]); if(ans[c]==-1)break; c++; } //printf("c=%I64d\n",c); //for(i=0;i<c;i++)printf("ans[%d]=%I64d\n",i,ans[i]); // return 0; while(scanf("%I64d",&n)!=EOF&&n>0) { lld min=n-13; if(min<0)min=-min; for(i=0;i<c;i++) { lld tmp=n-ans[i]; if(tmp<0)tmp=-tmp; if(tmp<min)min=tmp; } printf("%I64d\n",min); } return 0; }
第五题:郑厂长系列故事——体检
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4519
题解:一上来我让题目看的一题,我直接看5题。。。一看出题人那么少。。我还惊了一下,后来反应过来。。大家都去参加百度之星了。。。其实第五题很水。。很水。。比第一题水多了。。一共那么几行代码,做出来之后还不放心,找了好多测试数据试了一下,都没问题,提交,1a。。。。
主要就是分两种情况,一种n >= m 的时候,另一种相反。。。代码在这,很简单。。自己看行了。。
#include <cstdio> #include <cmath> #include <iostream> using namespace std; int n, k, m; int main() { int t; scanf("%d", &t); while (t--) { scanf("%d %d %d", &n, &k, &m); int ans; if (n >= m) { double tmp = (double)n*k / m; ans = (int)(tmp+0.999999999);//向上取整 } else { ans = k; } printf("%d\n", ans); } return 0; }
终于有次进复赛了,托了百度之星的福。。。如果我不迟到成绩还能好点~~~嘎嘎~~~