2013腾讯编程马拉松初赛第3场(3月23)(HDU 4515 HDU4516 HDU4517 HDU4518 HDU4519)


第一题:小Q系列故事——世界上最遥远的距离

链接 :http://acm.hdu.edu.cn/showproblem.php?pid=4515

题解:由于水题,直接贴roro大牛的代码

#include 
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 
#include 
#include 
#include 
#include 
#include 
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() {
    vectorv;
    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=4517

题解:

用num[i][j]存放1~i * 1~j里的黑点数目。

然后对枚举,

if (num[i+x-1][j+y-1] - num[i+x-1][j-1] - num[i-1][j+y-1] + num[i-1][j-1] == x*y) 

       ans++;

#include 
#include 
using namespace std;
const int maxn = 2005;

int n, m, x, y;
char map[maxn][maxn];
int num[maxn][maxn];

int main()
{
	memset(num, 0, sizeof(num));
	while (scanf("%d %d", &n, &m), n||m)
	{
		scanf("%d %d", &x, &y);
		getchar();
		int i, j;
		for (i=1; i<=n; i++)
		{
			for (j=1; j<=m; j++)
			{
				scanf("%c", &map[i][j]);
				if (map[i][j] == '*')
					num[i][j] = num[i][j-1] + num[i-1][j] - num[i-1][j-1] + 1;
				else
					num[i][j] = num[i][j-1] + num[i-1][j] - num[i-1][j-1];
			}
			getchar();
		}
		int ans = 0;
		int multi = x * y;
		for (i=1; i<=n; i++)
		{
			if (i + x - 1 > n) break;
			for (j=1; j<=m; j++)
			{
				if (j + y - 1 > m) break;
				if (num[i+x-1][j+y-1] - num[i+x-1][j-1] - num[i-1][j+y-1] + num[i-1][j-1] == multi) ans++;
			}
		}
		if (x != y)
		{
			x += y; y = x - y; x -= y;
			for (i=1; i<=n; i++)
			{
				if (i + x - 1 > n) break;
				for (j=1; j<=m; j++)
				{
					if (j + y - 1 > m) break;
					if (num[i+x-1][j+y-1] - num[i+x-1][j-1] - num[i-1][j+y-1] + num[i-1][j-1] == multi) 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
#include
#include
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=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;i0)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;i0;i--)
    {
        j=0;
        if(i==len)j++;
        for(;j0)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;i0)
    {
        lld min=n-13;
        if(min<0)min=-min;
        for(i=0;i




第五题:郑厂长系列故事——体检

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4519

题解:一上来我让题目看的一题,我直接看5题。。。一看出题人那么少。。我还惊了一下,后来反应过来。。大家都去参加百度之星了。。。其实第五题很水。。很水。。比第一题水多了。。一共那么几行代码,做出来之后还不放心,找了好多测试数据试了一下,都没问题,提交,1a。。。。

    主要就是分两种情况,一种n >= m 的时候,另一种相反。。。代码在这,很简单。。自己看行了。。

#include 
#include 
#include 
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;
}

终于有次进复赛了,托了百度之星的福。。。如果我不迟到成绩还能好点~~~嘎嘎~~~


你可能感兴趣的:(ACM,我的acm回顾)