2015上海大都会邀请赛参赛总结
第一次参加邀请赛,我们队还是依旧太弱了,感觉还有很多东西需要去看。
事后补一下题目。
A. A - Article
题目意思:DRD使用word去敲一篇文章,文章有n个字符,在i+0.1的时刻可以敲一个字符,在i-0.1的时刻,word有p的概率会崩溃,如果崩溃就会重启,返回上一次保存的地方,在i时刻,可以选择按ctrl-s去保存文章,需要说明的是ctrl-s需要按x个字符,而这x个字符可以在i时刻瞬间完成,并且不会崩溃,问DRD如果想敲完这篇文章,最少的敲字符期望个数。
思路: 这道题,每次看都有新的发现, 先不考虑保存,那么敲完i个字符的期望为 f[i]=(f[i-1]+1)*(1-p) + (f[i-1]+1+f[i])*p; 为什么会这样? 首先考虑,不崩溃的情况,就是i-1个字符的期望再敲一个字符,不崩溃的概率为1-p, 然后考虑,崩溃的情况, 如果崩溃的话, 敲的字符个数就为f[i-1]+1+f[i], 概率为p, 综合考虑后,就可以列出来这个式子。
那么如果考虑保存F[i]=f[i]+x;
如果保存, 那么每按一个ctrl-s就可以看做一个阶段, 每个阶段之间互相没有影响,每个阶段的期望可以直接相加。
第一种思路: 枚举或者三分ctrl-s的个数, 要想期望最少,贪心的思路是平均分配, 注意怎么平均分配。
第二种思路: DP dp[i]=min(dp[i], dp[i-k]+f[k]+x)
代码:
#include<bits/stdc++.h> #define INF 0x7f7f7f7f using namespace std; const int N=100010; double f[N]; int n,x; double p; void init() { f[0]=0; for (int i=1;i<=n;i++) f[i]=(f[i-1]+1)/(1-p); } double solve(int m) { double sum=0; if (n%m==0) sum=m*(f[n/m]+x); else { sum=(n%m)*(f[n/m+1]+x); sum+=(m-n%m)*(f[n/m]+x); } return sum; } int main() { int T,Case=0; scanf("%d",&T); while(T--) { scanf("%d%lf%d",&n,&p,&x); init(); int l=1,r=n,m1,m2,m; double ans=INF,a1,a2; while(l<=r) { m=(l+r)/2; m1=(l+m)/2; m2=(m+r)/2; a1=solve(m1); a2=solve(m2); if (a1-a2>=0.000001) { ans=min(ans,a2); l=m1+1; } else { ans=min(ans,a1); r=m2-1; } } //double ans=INF; //for (int i=1;i<=n;i++) ans=min(ans,solve(i)); printf("Case #%d: %0.6lf\n",++Case,ans); } return 0; }
B B - Base64
题意: 把字符串s通过base64规则转换k次并且输出。 JAVA貌似有专门的类库~
比赛的时候, 手速太慢
代码:
#include <iostream> #include <string.h> #include <stdio.h> #include <string.h> using namespace std; char f[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char s[2][1000]; int len[2]; void solve(int p) { int sum=0,dig=5; memset(s[p^1],0,sizeof(s[p^1])); len[p^1]=0; for (int i=0;i<len[p];i++) { for (int j=7;j>=0;j--) { sum=sum+(((s[p][i]>>j)&1)<<dig); dig--; if (dig<0) { s[p^1][len[p^1]++]=f[sum]; sum=0; dig=5; } } } if (dig==3) { s[p^1][len[p^1]++]=f[sum]; s[p^1][len[p^1]++]='='; s[p^1][len[p^1]++]='='; } if (dig==1) { s[p^1][len[p^1]++]=f[sum]; s[p^1][len[p^1]++]='='; } } int main() { int T,Case=0,p,k; scanf("%d",&T); while(T--) { p=0; scanf("%d%s",&k,s[p]); len[0]=strlen(s[0]); len[1]=0; while(k--) { solve(p); p=p^1; } printf("Case #%d: %s\n",++Case,s[p]); } return 0; } /* 5 1 A 1 AA 2 A 1 Mike 4 Mike */
C. C - Calculator
待完成
D D - Doom
待完成
E E - EXAM
题意: DRD需要准备N场考试, 给出每场考试开始的时间ei和持续的时间li, 和最少需要准备的时间ri, 复习的时间可以不连续, 但是考试的时间一定是连续的, 问是否会挂科
题解:全场最水的模拟题, but , 因为Case后面没有加#号, W了好几发, 我和队友刚开始都没有检查出来, 真是手残。 另外在比赛后重现时"NO“ 打成"N0"~~~
代码:
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int N=100010; long long f[N]; struct node { long long r,e,l; }; node p[N]; bool cmp(node a, node b) { return a.e<b.e; } int main() { int T,n,Case=0; bool flag; cin>>T; while(T--) { memset(f,0,sizeof(f)); cin>>n; for (int i=1;i<=n;i++) scanf("%I64d%I64d%I64d",&p[i].r,&p[i].e,&p[i].l); sort(p+1,p+n+1,cmp); flag=true; for (int i=1;i<=n;i++) { f[i]=f[i-1]+p[i].r; if (f[i]>p[i].e) { flag=false; break; } f[i]=f[i]+p[i].l; } if (flag) printf("Case #%d: YES\n",++Case); else printf("Case #%d: N0\n",++Case); } return 0; }
F F - Friends
题意: 给出10个人之间掌握语言的一个关系图,问一共可能有多少种情况
题解: 可以这样考虑, 每种语言之间都是相互独立的, 那么就可以利用乘法法则, 一种语言的情况为32, 那么N种就为32^N
代码:
import java.util.*; import java.math.*; public class Main { public static void main(String args[]) { Scanner cin = new Scanner(System.in); BigInteger sum; BigInteger p= new BigInteger("32"); int T,Case=0,n; T=cin.nextInt(); while(true) { T--; if (T<0) break; n=cin.nextInt(); sum=BigInteger.ONE; for (int i=1;i<=n;i++) sum=sum.multiply(p); Case++; System.out.println("Case #"+Case+": "+sum); } } }
G - Game
题意: 给出一颗树, 从根找出K条路径, 每条路径上的值加起来使其最大, 有一个地方需要注意, 同一个结点的值不能重复加。
题解:
注意 使用long long
第一种方法是使用线段树来维护更新
首先DFS, 记录下每个结点的儿子结点DFS序的区间,和每个点的父亲结点,以及从跟到每个结点的值。
把区间(1,n) 建立一颗线段树, 这里的1和n为每个结点的DFS序, 在询问每个结点信息的时候需要translate下, 在线段树中需要维护的是一个最大值和最大值的ID。
K次取最大值操作, 每次都需要把根结点和父亲结点所在的DFS序区间更新。 每个结点的信息只更新一次。
输出最后结果。
第二种方法是一种比较巧妙的自下而上的更新方式, 学习了
第一种代码:
#include <stdio.h> #include <string.h> #define LL long long using namespace std; const int N=100010; struct node { int id; LL sumv,pushv; }; int Next[N],to[N],head[N],id,idx; int fa[N],ll[N],rr[N],translate[N]; LL w[N],sum[N]; node tree[N<<2]; void init() { memset(head,0,sizeof(head)); id=1; idx=0; } void addedge(int a, int b) { Next[id]=head[a]; to[id]=b; head[a]=id++; } void dfs(int u, int pre, LL tmp) { fa[u]=pre; sum[u]=tmp+w[u]; ll[u]=++idx; for (int i=head[u];i!=0;i=Next[i]) dfs(to[i],u,sum[u]); rr[u]=idx; } void build(int v, int l, int r) { if (l==r) { tree[v].sumv=sum[translate[l]]; tree[v].pushv=0; tree[v].id=translate[l]; return; } tree[v].pushv=0; int mid=(l+r)/2; build(v*2,l,mid); build(v*2+1,mid+1,r); if (tree[v*2].sumv>tree[v*2+1].sumv) { tree[v].id=tree[v*2].id; tree[v].sumv=tree[v*2].sumv; } else { tree[v].id=tree[v*2+1].id; tree[v].sumv=tree[v*2+1].sumv; } } void push_down(int v) { if (tree[v].pushv!=0) { tree[v*2].pushv+=tree[v].pushv; tree[v*2+1].pushv+=tree[v].pushv; tree[v*2].sumv+=tree[v].pushv; tree[v*2+1].sumv+=tree[v].pushv; } tree[v].pushv=0; } void update(int v, int l, int r, int L, int R,LL value) { if (L<=l&&r<=R) { tree[v].sumv+=value; tree[v].pushv+=value; return; } push_down(v); int mid=(l+r)/2; if (L<=mid) update(v*2,l,mid,L,R,value); if (R>mid) update(v*2+1,mid+1,r,L,R,value); if (tree[v*2].sumv>tree[v*2+1].sumv) { tree[v].id=tree[v*2].id; tree[v].sumv=tree[v*2].sumv; } else { tree[v].id=tree[v*2+1].id; tree[v].sumv=tree[v*2+1].sumv; } } int main() { int T,Case=0,n,k,a,b; scanf("%d",&T); while(T--) { init(); scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) scanf("%I64d",&w[i]); for (int i=1;i<n;i++) { scanf("%d%d",&a,&b); addedge(a,b); } dfs(1,0,0); for (int i=1;i<=n;i++) translate[ll[i]]=i; n=idx; build(1,1,n); LL ans=0; while(k--) { ans+=tree[1].sumv; int u=tree[1].id; while(u!=0&&w[u]!=0) { update(1,1,n,ll[u],rr[u],-w[u]); w[u]=0; u=fa[u]; } } printf("Case #%d: %I64d\n",++Case,ans); } return 0; } /* 4 5 2 4 3 2 1 1 1 2 1 5 2 3 2 4 5 3 4 3 2 1 1 1 2 1 5 2 3 2 4 5 2 4 3 2 1 1 1 2 1 5 2 3 2 4 5 3 4 3 2 1 1 1 2 1 5 2 3 2 4 */
第二种:
#include <stdio.h> #include <string.h> #include <queue> using namespace std; int n, k; const int N = 100000 + 10; struct edge { int next, to; }e[2*N]; int head[N], tot; void init() { memset(head, 0, sizeof(head)); tot = 1; } void add(int u, int v) { e[tot].next = head[u]; e[tot].to = v; head[u]= tot++; } int a[N], fa[N]; struct node { int id; long long maxv; }Q[N]; long long maxv[N]; void dfs(int u, int p) { fa[u] = p; Q[u].id = -1; for(int i = head[u]; i != 0; i = e[i].next) if(e[i].to != p) { dfs(e[i].to, u); if(Q[u].id == -1 || Q[u].maxv < Q[e[i].to].maxv) { Q[u].id = Q[e[i].to].id; Q[u].maxv = Q[e[i].to].maxv; } } if(Q[u].id == -1) { Q[u].maxv = 0; Q[u].id = u; } Q[u].maxv += a[u]; } bool operator < (node a, node b) { return a.maxv < b.maxv; } bool vis[N]; int main() { int T; scanf("%d", &T); for(int cas = 1; cas <= T; cas++) { init(); scanf("%d%d", &n, &k); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); int u, v; for(int i = 1; i < n; i++) { scanf("%d%d", &u, &v); add(u, v); } dfs(1, 0); priority_queue<node> que; que.push(Q[1]); long long ans = 0; while(k--) { if(que.empty()) break; node tmp = que.top(); que.pop(); ans += tmp.maxv; u = tmp.id; while(fa[u] !=0) { v = fa[u]; for(int i = head[v]; i != 0; i = e[i].next) if(e[i].to != u) { que.push(Q[e[i].to]); fa[e[i].to] = 0; } fa[u] = 0; u = v; } } printf("Case #%d: %I64d\n", cas, ans); } return 0; }
H - Homework
待完成
I - inverse
待完成
J - Joyful
题意: 给出一个N*M的矩阵, 进行K此操作,每次操作随机选取两个点(x1,y1) (x2,y2), 两个点组成一个矩形, 这个矩形内的数字就被选中, 每个矩形单元内的数字选中只能被记为一次, 问进行K此操作,选中点个数的期望。
题解: 每个点都是相互独立的, 可以以这个点为中心,划分为9个区域, 分别算出来每个点被一次选中的概率p, 那么两次就是1-(1-p)^2, 最后累加起来就是最后结果。
代码:
#include <cstdio> #include <iostream> #include <string.h> using namespace std; int main() { int T,Case=0; long long n,m,k; double p,ans; scanf("%d",&T); while(T--) { scanf("%I64d%I64d%I64d",&m,&n,&k); ans=0; double c=n*m*n*m*1.0,pp; for (long long i=1;i<=m;i++) for (long long j=1;j<=n;j++) { p=0; p+=(i-1)*(j-1)*(m-i+1)*(n-j+1)*1.0/c; p+=(i-1)*(m-i+1)*n*1.0/c; p+=(i-1)*(n-j)*(m-i+1)*j*1.0/c; p+=(j-1)*m*(n-j+1)*1.0/c; p+=(n*m*1.0)/c; p+=(n-j)*m*j*1.0/c; p+=(m-i)*(j-1)*i*(n-j+1)*1.0/c; p+=(m-i)*i*n*1.0/c; p+=(m-i)*(n-j)*i*j*1.0/c; p=1-p; pp=p; for (long long t=2;t<=k;t++) p=p*pp; ans=ans+1.0-p; } printf("Case #%d: %0.lf\n",++Case,ans); } return 0; }