9th NENU ACM-ICPC Contest Solving Report
来源:互联网 发布:简单的数据库系统 编辑:程序博客网 时间:2024/05/22 09:07
第九届东北师范大学ACM-ICPC校赛于2015年4月12日圆满落幕,五个小时赛程共十道题,现公布题解如下:
--------------------------------------------------------------------------------------
绪→感谢出题组、验题组、翻译组的倾力奉献,人员如下:
Ok_again Alwayswet
Cs1131 Vencentuse
Light_starlight Dream410
Yinzm Junkie
Todayac Cuijy
Fengcb
--------------------------------------------------------------------------------------
A题:
Author:Light_starlight
Interpreter:Light_starlight
设定为签到题,故事背景为"曹冲称象",给出N个石头的重量,求大象的重量。
N个数求和,目标所有选手可AC。
#include<iostream>#include<cstdio>#include<cmath>#include<cstring>using namespace std;int main(){ int n; while(~scanf("%d",&n)){ int x; int sum = 0; while(n--){ scanf("%d",&x); sum += x; } printf("%d\n",sum); } return 0;}
B题:
Author:Cuijy
Interpreter:Light_starlight
输出pascal triangle的第N行,N∈(0,100000].answer mod 1e9+7
众人皆知,杨辉三角的通式为:第n行第m个数位C(n-1,m-1)。
那么本题就变成了求C(n,m)显然:
C(n, m) mod p = n!/(m!(n - m)!) mod p
由于N范围达10^5,朴素的递推求解时间代价太高
考虑1e9+7为素数,根据费马小定理: 若p是质数,且gcd(a,p)=1,则a^(p-1)≡ 1 (mod p),即a*ap-2 ≡ 1 (mod p)
就是说,(m!(n-m)!)的逆元为 (m!(n-m)!)p-2
C(n,r)=(n!)*(r!)^(P-2)*(n-r)!^(P-2)%P
接下来的处理就简单明了了。
#include<math.h>#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;typedef long long LL;const LL mod=1e9+7;const int maxn=1111111;LL Inv(LL x, LL mod) { LL r, y; for(r = 1, y = mod - 2; y; x = x * x % mod, y >>= 1) (y & 1) && (r = r * x % mod); return r;}LL A[maxn],C[maxn];void init(){ A[0]=1,A[1]=1;C[0]=Inv(A[0],mod);C[1]=Inv(A[1],mod); for(int i=2;i<maxn;i++){ A[i]=A[i-1]*i%mod; C[i]=Inv(A[i],mod); }}int main() { int n; init();// freopen("cjy.in","r",stdin);// freopen("cjy.out","w",stdout); while(scanf("%d",&n)!=EOF) { printf("1"); for(int i=1;i<=n;i++){ printf(" %lld",A[n]*C[n-i]%mod*C[i]%mod); } puts(""); }}
C题:
Auther:Fengcb
Interpreter:Light_starlight
一个数字最多表示成4个数字的平方和的种数。
DP预处理,定义dp[i][j]表示到数字i时,当前有j个数的平方和。
状态转移方程为:dp[i][j] = ∑{dp[i-k*k][j-1]},时间复杂度为O(4Nsqrt(N)).
三层sqrt暴力+break优化也是可以过的,时间复杂度为O(Nsqrt(N)).
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define N 40000using namespace std;int dp[N][5];int main() { int n,i,j,k; memset( dp,0,sizeof(dp) ); dp[0][0]=1; for( i=1; i*i<N; i++ ) for( j=1; j<5; j++ ) for( k=i*i; k<N; k++ ) dp[k][j]+=dp[k-i*i][j-1]; int t; cin>>t; while(t--) { cin>>n; printf( "%d\n",dp[n][1]+dp[n][2]+dp[n][3]+dp[n][4] ); } return 0;}
D题:
Author:Todayac
Interpreter:Light_starlight
有N个整数构成一个数列A,给出一个整数S。在这个数列中是否存在两个数的和等于S.
直接用Map或者Set就可以过了,当然现场大部分人用的是二分查找。
不管什么姿势,综合复杂度均为O(NlogN)
以下给出一种优雅的写法:
#include<algorithm>#include<iostream>#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>using namespace std;#ifdef __int64typedef __int64 LL;#elsetypedef long long LL;#endif#define M 100005int a[M];int main() { int T, N, S; while(~scanf("%d", &N)) { for(int i = 0; i < N; i ++) { scanf("%d", &a[i]); } scanf("%d", &S); sort(a, a+N); int flg = 0; for(int i = 0, j = N-1 ; i < j; ) { if(a[i] + a[j] == S) { flg = 1; break; } else if(a[i] + a[j] < S) i++; else j--; } //printf("%d: ",T); if(flg == 0) printf("No\n"); else printf("Yes\n"); } return 0;}
E题:
Author:Alwayswet
Interpreter:Light_starlight
N个点,求出每对点之间的距离的平方和。
N∈[1,100000],坐标X,Y∈[-10000,10000].
非常有意思的推导题,乍一看无从下手,拿三四个点算一算,发现很容易O(N)解决
推导结果不多说,看代码就悟了~
#include<stdio.h>#include<math.h>#include<algorithm>using namespace std;typedef __int64 ll;struct point{ ll x,y,sum,sum2; void input(){ scanf("%I64d%I64d",&x,&y); sum=x+y; sum2=x*x+y*y; }}p[100100];int main(){ int n; while(scanf("%d",&n)!=EOF){ ll _sumx=0,_sumy=0,_sum2=0; for(int i=0;i<n;i++){ p[i].input(); _sumx+=p[i].x; _sumy+=p[i].y; _sum2+=p[i].sum2; } ll ans=0; for(int i=0;i<n;i++){ ans+=_sum2+p[i].sum2*n; ans-=(_sumx)*2*p[i].x; ans-=(_sumy)*2*p[i].y; } printf("%I64d\n",ans/2); }}
F题:
Author:Alwayswet
Interpreter:Yinzm
加农炮前有M堵墙,以恒定速度打出N发炮弹,倾斜角度0°到45°之间,求每个炮弹的落点。
炮弹打到地上就在地上,打到墙上就黏在墙上,N,M∈[1,10000],g=9.8.
暴力O(N*M)是不科学的
由于速度恒定,那么炮弹在0°到45°之间打出的距离是存在单调性的
将炮弹按照角度排序,将墙按距离原点递增排序
然后离线操作,单调队列O(N+M)不断将炮弹与墙不断往后算即可
最后再按照原来的顺序输出落点
#include<math.h>#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;const double eps=1e-8;const double pi=acos(-1.0);const double g=9.8;int sgn(double x){ if(x<-eps) return -1; if(x>eps) return 1; return 0;}struct point{ double x,y; point(double _x=0,double _y=0){ x=_x,y=_y; } bool friend operator<(const point &a,const point &b){ return a.x<b.x; } void input(){ scanf("%lf%lf",&x,&y); }}p[120000],ans[120000];struct cannon{ double angle; int id; bool friend operator<(const cannon &a,const cannon &b){ return a.angle<b.angle; } void input(int _id){ scanf("%lf",&angle); id=_id; }}q[120000];int n,m;double v;int main(){// freopen("E.in","r",stdin);// freopen("E.out","w",stdout); while(scanf("%d%lf",&n,&v)!=EOF){ for(int i=0;i<n;i++) q[i].input(i); scanf("%d",&m); for(int i=0;i<m;i++) p[i].input(); sort(p,p+m);sort(q,q+n); int j=0; for(int i=0;i<n;i++){ double k=tan(q[i].angle); double b=(1+k*k)*g/2/v/v; bool f=0; while(j<m){ double ty=k*p[j].x-b*p[j].x*p[j].x; if(sgn(ty)<=0){ int id=q[i].id; ans[id].y=0; ans[id].x=k/b; f=1; break; } if(sgn(p[j].y-ty)>=0){ int id=q[i].id; ans[id].y=ty; ans[id].x=p[j].x; f=1; break; } j++; } if(!f){ int id=q[i].id; ans[id].y=0; ans[id].x=k/b; } } for(int i=0;i<n;i++){ printf("%.6f %.6f\n",ans[i].x,ans[i].y); } }}
G题:
Author:Yinzm
Interpreter:Junkie
这个题目是一道特别好的思维题,考点是高斯消元,建议大家先去切几道基本的高斯消元题目,再来研究这个题目。这个题目必须得真正理解高斯消元的思想才能快速的切掉。因为数据量比较大,所以用其他方法不太适合。如果只挑选两个数字的话,就可以考虑用字典树(如去年的校赛中的某题),如果数据量比较小(<=30)的,还能用状态压缩+字典树解决,强烈推荐大家去亲手切两道这种题目。在比赛中异或运算应该算出现频率比较高的了,基于异或运算的性质,所以我们一般都要往二进制上面想。
因为要在N个数中挑选若干个数字一起异或,所以可以先考虑将所有的数字都用二进制表示,根据题目中的数据范围,将所有的二进制数都表示为32位,如果不足32位的高位补零,得到N个长度为32位的二进制数。现在需要考虑的是怎么将最后的结果最大,最后的结果如果也用二进制表示的话,我们的期望当然是高位的1越多越好。高斯消元的思想现在就能用上了,建立方程组,然后判断最后结果高位能不能取1。
1、建立方程组矩阵。我们可以将这N个数从高位到低位建立方程,假设低i个数的二进制表示为[ a(i,1) a(i,2) ... a(i,32) ],由于期望最后结果的每一个位都能取1,所以将最后结果的那一列全部置为1。方程的n个变量x1...xn代表该位取或者不取,即xi=0或xi=1
2、从第一个方程开始判断能够成立,能够成立就代表最后结果中的该位能够取1,否则取0。判断方法,从方程左边开始查找,若a(i,j)=1而且方程右端为1,那么这个方程肯定可以成立(只需要让xi变量取1,其他变量都取0便可),然后利用该方程向下消元(因为该变量的值已经确定了,便消去它)。若没有a(i,j)=1,但是此时方程右端为0,那么此时这个方程也能成立(所有的变量都取0便可)。最后将结果的二进制表示转化为十进制,就可以输出了。
#include<cstdio>#define LL long longusing namespace std;int n;LL x;int a[110][110];void Guass(){ LL ans=0; for(int i=0;i<=33;i++){ int id=-1; for(int j=0;j<n;j++){ if(a[i][j]){ id=j; break; } } if(id==-1 && a[i][n]==0){ ans|=(1ll<<(33-i)); }else if(id!=-1){ ans|=(1ll<<(33-i)); for(int j=i+1;j<=33;j++){ if(a[j][id]){ for(int k=0;k<=n;k++){ a[j][k]^=a[i][k]; } } } } } printf("%lld\n",ans);}int main(){// freopen("input.txt","r",stdin);// freopen("output.txt","w",stdout); while(~scanf("%d",&n)){ for(int i=0;i<n;i++){ scanf("%lld",&x); for(int j=33;j>=0;j--){ a[(33-j)][i]=(x>>j)&1; } } for(int i=0;i<=33;i++)a[i][n]=1; Guass(); } return 0;}
H题:
Author:ok_again
Interpreter:Junkie
十分有趣的小学追击类问题,三个条件列出一个方程组,然后不断消去未知数,得到:L = 2*v*T^2/t
一行printf结束,就是这么easy!
#include<algorithm>#include<cstdio>int main() {// freopen("in.in", "r", stdin);// freopen("out.out", "w", stdout); double T, V, t; while(scanf("%lf%lf%lf", &T, &V, &t) != EOF) { printf("%.5f\n", 2 * T * T * V / t); } return 0;}
I题:
Author:ok_again
Interpreter:Junkie
模拟题,题目怎么说,代码就怎么写//#pragma comment(linker, "/STACK:1024000000,1024000000")#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<vector>#include<string>#include<queue>#include<cmath>#include<stack>#include<set>#include<map>#define FIR first#define SEC second#define MP make_pair#define INF 0x3f3f3f3f#define LL long long#define CLR(a, b) memset(a, b, sizeof(a))using namespace std;const int maxn = 3333;int L[maxn], R[maxn], bank[maxn], head[7], cnt[7];void init() { for(int i = 1; i <= 5; i ++) { head[i] = 3000 + i; bank[i + 3000] = i; cnt[i] = 0; } for(int i = 0; i < maxn; i ++) { L[i] = R[i] = i; }}void ins(int x, int y) { L[y] = x; R[y] = R[x]; L[R[x]] = y; R[x] = y; bank[y] = bank[x]; cnt[bank[x]] ++;}void del(int x) { cnt[bank[x]] --; R[L[x]] = R[x]; L[R[x]] = L[x];}int getnum(int x) { int ret = 0, Lx = L[x]; while(Lx != head[bank[x]]) {// printf("%d == %d\n", Lx, head[bank[x]]); Lx = L[Lx]; ret ++; } return ret;}int main() {// freopen("in2.txt", "r", stdin);// freopen("out2.txt", "w", stdout); int m; while(scanf("%d", &m) != EOF) { init(); while(m --) { char op[10]; int x, b; scanf("%s", op);// printf("%s == ", op); if(op[0] == 'O') { if(op[2] == '1') { scanf("%d", &x); int hd = -1, nm = maxn; for(int i = 1; i <= 5; i ++) { if(nm > cnt[i]) { nm = cnt[i]; hd = i; } } ins(L[head[hd]], x); printf("%d\n", cnt[bank[x]] - 1); } else if(op[2] == '2') { scanf("%d%d", &x, &b); if(bank[x] == b) { puts("Something Wrong!!!"); } else { del(x); ins(L[head[b]], x); printf("%d\n", cnt[bank[x]] - 1); } } else if(op[2] == '3') { scanf("%d", &b); if(cnt[b] == 0) { puts("Something Wrong!!!"); } else { del(R[head[b]]); printf("%d\n", cnt[b]); } } else { scanf("%d", &x); del(x); printf("%d\n", cnt[bank[x]]);// printf("%d\n", getnum(x)); } } else { if(op[1] == '1') { scanf("%d", &b); printf("%d\n", cnt[b]); } else { scanf("%d", &x); printf("%d\n", getnum(x)); } } } }}
J题:
Auther:dream410
Interpreter:Light_starlight
思路非常好的博弈论题。
运用群论中置换的思想,可用LCM(最小公倍数)求出置换回最初状态需要的置换步数,然后题目就转换为了巴什博弈。
#include<cstdio>#include<cstring>#include<algorithm>#define maxn 1005using namespace std;int p[maxn];bool used[maxn];int main() { int n, m;// freopen("in.txt","r",stdin);// freopen("out.txt","w",stdout); while(~scanf("%d %d",&n,&m)) { for(int i=1; i<=n; i++) { scanf("%d",&p[i]); } memset(used,false,sizeof(used)); int ans=1; for(int i = 1; i <= n; i ++) { int tmp = 0; while(!used[i]) { used[i] = true; tmp ++; int q = p[i]; while(!used[q]) { used[q] = true; tmp ++; q = p[q]; } ans = ans / __gcd(ans, tmp) * tmp; } }// printf("%d\n",ans); if((ans - 1) % (m + 1) ==0)printf("Bob\n"); else printf("Alice\n"); }}/*5 34 1 5 2 3*/
- 9th NENU ACM-ICPC Contest Solving Report
- The 35th ACM/ICPC Asia Regional Tianjin Site —— Online Contest
- The 36th ACM/ICPC Asia Regional Dalian Site —— Online Contest
- The 36th ACM/ICPC Asia Regional Dalian Site —— Online Contest hdu4001
- The 36th ACM/ICPC Asia Regional Dalian Site —— Online Contest hdu4002
- The 36th ACM/ICPC Asia Regional Dalian Site —— Online Contest hdu4007
- HDU 3681 Prison Break 二分+搜索 The 35th ACM-ICPC Asia Regional Contest (Hangzhou)
- HDU4021 24 Puzzle The 36th ACM/ICPC Asia Regional Shanghai Site —— Online Contest
- HDU4023 Game The 36th ACM/ICPC Asia Regional Shanghai Site —— Online Contest
- HDU 4034 Graph The 36th ACM/ICPC Asia Regional Chengdu Site —— Online Contest
- HDU 4038 Stone The 36th ACM/ICPC Asia Regional Chengdu Site —— Online Contest
- 【转】The 36th ACM/ICPC Asia Regional Chengdu Site —— Online Contest 出题报告
- The 36th ACM/ICPC Asia Regional Chengdu Site —— Online Contest
- The 36th ACM/ICPC Asia Regional Beijing Site Online Contest - G Panda
- The 36th ACM/ICPC Asia Regional Beijing Site Online Contest - B Eliminate Witches!
- The 36th ACM/ICPC Asia Regional Beijing Site Online Contest - G Panda
- The 36th ACM/ICPC Asia Regional Beijing Site Online Contest - B_Eliminate Witches!
- 【树状数组】The 36th ACM/ICPC Asia Regional Beijing Site Online Contest - G Panda
- Eclipse配置hadoop2.2.0环境
- [线段树+离线处理] hdu 4417 Super Mario
- Android 调试工具 Stetho 使用学习
- SIM卡中UCS2编码的三种格式(80,81,82)分析
- ASP.NET Session简介丶特性
- 9th NENU ACM-ICPC Contest Solving Report
- 如何加密Android apk
- 第37课时,自测
- hdu3336 Count the string(KMP应用)
- 黑马程序员 C语言 指针1
- 安卓之listview和textview争抢焦点的解决办法
- hadoop编程实例
- 使用Angular-Leaflet-directive获取select选中的值
- Linux下重启tomcat的shell脚本