2013 ACM-ICPC 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; }
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; }
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; }
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; }