2013 ACMICPC China Nanjing Invitational Programming Contest 总结

A.Play the Dice (HDU 4586)

思路:

  很裸的一道概率题,容易得到公式ans=1/n*va[1]+1/n*va[2]+...+1/n*va[n]+m/n*ans,va[i]为对应位置得到的钱数,但是细节不得不注意!!第一次没看到无限多钱要输出inf,第二次没想到,即使n==m也不一定无限多钱,因为有可能每个面得到的钱数都是0.

#include<stdio.h>
#include<string.h>
int va[300];
int main()
{
    int n,i,m,LL;
    while(scanf("%d",&n)!=EOF)
    {
        double ans=0;
        for(i=1;i<=n;i++){
            scanf("%d",&va[i]);
            ans+=1.0*va[i];
        }
        scanf("%d",&m);
        for(i=0;i<m;i++)
            scanf("%d",&LL);
        if(n==m)
        {
            if(ans==0)
                printf("0.00\n");
            else
                printf("inf\n");
            continue;
        }
        ans=ans/(1.0*(n-m));
        printf("%.2lf\n",ans);
    }
    return 0;
}
NJUST 1737


B.TWO NODES (HDU 4587)

思路:

  图论渣渣...等学了图论再看..

C.Count The Carries (HDU 4588)

思路:

  这题一直在想数位dp神马的,后来才发现其实是有规律的....1-n转化成2进制以后,每一位1出现都是循环的,所以可以直接求出1-n之间所有数转化成2进制后,每一位1的个数..然后就可以直接算进位的次数了.

#include<stdio.h>
#include<string.h>
long long sum[100];
long long solve(int a,int b)
{
    memset(sum,0,sizeof(sum));
    long long cir=2;
    if(b>=0)
    {
        for(int i=1;i<=40;i++)
        {
            if(2*b<cir) break;
            sum[i]+=b/cir*(cir/2);
            int temp=b%cir;
            if(temp>(cir/2))
                sum[i]+=temp-(cir/2);
            cir*=2;
        }
    }
    cir=2;
    if(a>=0)
    {
        for(int i=1;i<=40;i++)
        {
            if(2*a<cir) break;
            sum[i]-=a/cir*(cir/2);
            int temp=a%cir;
            if(temp>(cir/2))
                sum[i]-=temp-(cir/2);
            cir*=2;
        }
    }
    long long ret=0;
    int pos=1;
    while(sum[pos]!=0)
    {
        ret+=sum[pos]/2;
        sum[pos+1]+=(sum[pos]/2);
        pos++;
    }
    return ret;
}

int main()
{
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        printf("%lld\n",solve(a,b+1));
    }
    return 0;
}
NJUST 1739

G.Boring Game (HDU 4592)
思路:

  大家第一下一定都会想到枚举第一行的状态改变...目测这题卡的就是这种做法...TLE了好久,其实,不管哪种状态,最后都可以处理成,前n-1行全是0的状态,而第n行不确定,由于第n行的状态只有1<<n种,所以可以预处理出除了最后一行,其它行全是0时,哪种状态改变可以让第一行变成0,然后保存下来.以上预处理过后,求解每个棋盘的时候,第一行状态不变,从第二行开始向下先处理一遍,就变成了我们预处理过的状态,再用我们预处理出来的状态处理回去就可以全变成0了.当然,两次改变的位置是要异或一下的,因为按2下等于没按嘛.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define INF 100000000
int va[10],n,tem[10],NUM1[300],tem1[10],tem2[10];
int MAP[10][300];
vector<int>road[10][300];
int min(int x,int y){
    return x<y?x:y;
}
int get1(int n){
    n=(n&0x55555555)+((n>>1)&0x55555555);
    n=(n&0x33333333)+((n>>2)&0x33333333);
    n=(n&0x0f0f0f0f)+((n>>4)&0x0f0f0f0f);
    n=(n&0x00ff00ff)+((n>>8)&0x00ff00ff);
    n=(n&0x0000ffff)+((n>>16)&0x0000ffff);
    return n;
}

void init(){
    int i,j,k,r;
    for(i=0;i<=255;i++){
        NUM1[i]=get1(i);
    }
    for(i=1;i<=8;i++)
    {
        int state=(1<<i)-1;
        for(j=0;j<=state;j++)
        {
            MAP[i][j]=j^((j<<1)&state)^(j>>1);
        }
        for(j=0;j<=state;j++)
        {
            for(k=0;k<=state;k++)
            {
                memset(tem,0,sizeof(tem));
                int pre=k;
                tem[1]=j^MAP[i][k];
                for(r=2;r<=i;r++)
                {
                    tem[r]^=pre;
                    tem[r]^=MAP[i][tem[r-1]];
                    pre=tem[r-1];
                }
                if(tem[i]==0)
                {
                    road[i][j].push_back(k);
                }
            }
        }
    }
}
int make(int a[],int b[],int n,int sta)//初始状态,中间状态
{
    int r;
    memset(tem,0,sizeof(tem));
    b[1]=sta;
    tem[1]=a[1]^MAP[n][sta];
    for(r=2;r<=n;r++)
    {
        tem[r]=a[r]^b[r-1];
        tem[r]^=MAP[n][tem[r-1]];
        b[r]=tem[r-1];
    }
    return tem[n];
}

int main()
{
    int T,i,j,ans;
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
            scanf("%d",&va[i]);
        memset(tem2,0,sizeof(tem2));
        tem2[1]=make(va,tem1,n,0);
        ans=INF;
        for(i=0;i<(int)road[n][tem2[1]].size();i++)
        {
            make(tem2,va,n,road[n][tem2[1]][i]);
            int cnt=0;
            for(j=1;j<=n;j++)
                cnt+=NUM1[tem1[n-j+1]^va[j]];
            ans=min(ans,cnt);
        }
        if(ans==INF) printf("-1\n");
        else printf("%d\n",ans);
    }
    return 0;
}
NJUST 1743


H.Robot (HDU 4593)

思路:

  这个就略过了..

K.Yet another end of the world (HDU 4596)

思路:

  看数据量一定是枚举任意2个虫洞有没有交集,对于2个虫洞,只要能找到x1*k1+a=x2*k2+b(k1,k2为任意整数)就说明有交集,对等式移项可得x1*k1-x2*k2=b-a,如果有整数解,那么(b-a)%gcd(x1,x2)应该等于0,否则无整数解,现在a的范围是[y1,z1],b的范围是[y2,z2],那么b-a的范围也可以确定下来,剩下的只要判断一下是否存在(b-a)%gcd(x1,x2)==0就行了,第一次写的时候没完全想清楚区间的转化,代码写的比较挫..

#include<stdio.h>
#include<iostream>
using namespace std;
struct node
{
    int x,y,z;
}va[1200];
long long gcd(long long x,long long y)
{
    while(x)
    {
        long long temp=x;
        x=y%x;
        y=temp;
    }
    return y;
}
int check(int i,int j)
{
    if(va[i].x==va[j].x){
        if(va[i].y<va[j].x)
            return 0;
        if(va[j].y<va[i].x)
            return 0;
        return 1;
    }
    long long tem=gcd(va[i].x,va[j].x);
    if(tem==1) return 1;
    long long max=va[j].z-va[i].y;
    long long min=va[j].y-va[i].z;
    if(min<0){
        long long k=(-min)/tem;
        max+=k*tem;
        min+=k*tem;
    }
    while(min<0){
        max+=tem;
        min+=tem;
    }
    if(min%tem==0||max%tem==0)
        return 1;
    if(min/tem!=max/tem)
        return 1;
    return 0;
}
int main()
{
    int n,i,j;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
            scanf("%d%d%d",&va[i].x,&va[i].y,&va[i].z);
        int flag=0;
        for(i=1;i<=n;i++){
            for(j=1;j<=i;j++){
                if(i==j) continue;
                if(check(i,j)){
                    flag=1;
                    break;
                }
            }
        }
        if(flag)
            printf("Cannot Take off\n");
        else
            printf("Can Take off\n");
    }
    return 0;
}
NJUST 1747
原文地址:https://www.cnblogs.com/SolarWings/p/3078859.html