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

  

posted on 2015-06-22 14:26  wzb_hust  阅读(199)  评论(0编辑  收藏  举报

导航