8.1个人排位赛09赛后题解

A. diffsum 2014新生暑假个人排位赛09

签到,注意分析朴素算法的时间复杂度,选择合适的计算方式。注意初始化位置。


时间限制 1000 ms  内存限制 65536 KB

题目描述

Mays王国的女王大人每天过着自由自在的生活,她最大的乐趣就是给邻国的帅气王子写信。但是最近,Mays王国的叔叔们变得很无聊,他们知道女王大人每次都把信委托给皇家小妹妹快递公司的小妹妹们,于是叔叔们给每一条路都设立了路障,只有小妹妹们给他们表演节目才会让小妹妹们过去。
在每一个路障,都有不同数量的叔叔,只有表演的小妹妹的数量不少与叔叔的数量的时候叔叔才会放她们过去。
可是七夕快要到了,Masy王国的每一个人都在给自己的情人写信。
Mays王国由n个地区组成,一些双向的道路连接着这些地区。注意两个地区之间可能有多条道路相通。
无聊的单身的beegerous已经算出了从一点到另一点送信最少需要的小妹妹的数量,为了表达对世界的不满,他脑补出了小妹妹们给任意两个点之间送信的画面,这样她们一共需要送n*(n-1)次信!如果所有的信件必须同一时间发出,这样每一次快递都需要由不同的小妹妹们来送!这样就会有好多好多小妹妹在Mays王国的道路上忙碌了。
请问,按上述脑补情形,皇家小妹妹快递公司一共需要派出多少小妹妹。

输入格式

输入第一行为数据组数T(T<=10),接下来T组数据,每组第一行为n,m,,2<=n<=10000,1<=m<=100000,表示Mays王国的道路由n个节点组成,接下来m行,每行一组u,v,c表示连接节点u,v的一条无向道路,且路障上有c个叔叔,1<=u,v<=n,0<=c<=100。
输入保证任意两点之间可达。

输出格式

每组数据输出一个数字,表示小妹妹快递公司最少需要派出的小妹妹数量。

输入样例

1
3 3
1 2 1
2 3 1
1 3 3

输出样例

6

hint:
9种送快递的方式及花费为:
1 --> 2:  1
1 --> 3:  1
2 --> 1:  1
2 --> 3:  1
3 --> 1:  1
3 --> 2:  1
答案为6
与第八场中的B题类似,同样注意特判,cost=0的情况。
另外开links[]数组,存并查集中的个数,便于计算边数,防止TLE
特别注意:有些标识符在库函数中有声明,如果自己声明变量时再用,会引发错误,导致CE
比如这里的link,unite之类的,用时做一些变换,links,unions,避免发生此类问题

代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;
struct edge
{
    int from,to,cost;
    bool operator < (const edge b)const
    {
        return cost<b.cost;
    }
}e[100010];
int father[10010],ranks[100010];
long long links[100010];
int finds(int x)
{
    if(x==father[x])
        return x;
    else
        return father[x]=finds(father[x]);
}
int same(int x,int y)
{
    x=finds(x);
    y=finds(y);
    return x==y;
}
void unions(int x,int y)
{
    x=finds(x);
    y=finds(y);
    if(x==y)
        return ;
    if(ranks[x]<ranks[y])
    {
       father[x]=y;
       links[y]+=links[x];
    }
    else
    {
        father[y]=x;
        links[x]+=links[y];
        if(ranks[x]==ranks[y])
            ranks[x]++;
    }
}
int main()
{
    int t,n,m,i;
    scanf("%d",&t);
    while(t--)
    {
        long long sum=0;
        memset(ranks,0,sizeof(ranks));
        scanf("%d %d",&n,&m);
        for(i=0;i<=n;i++)
        {
             links[i]=1;
             father[i]=i;
        }
        for(i=0;i<m;i++)
        {
            scanf("%d %d %d",&e[i].from,&e[i].to,&e[i].cost);
        }
        sort(e,e+m);
        for(i=0;i<m;i++)
        {
            if(!same(e[i].from,e[i].to))
            {
                if(e[i].cost==0)
                    sum+=links[finds(e[i].from)]*links[finds(e[i].to)]*2;
                else
                    sum+=links[finds(e[i].from)]*links[finds(e[i].to)]*2*e[i].cost;
                unions(e[i].from,e[i].to);
            }
        }
        printf("%lld\n",sum);
    }
    return 0;
}


时间限制 6000 ms  内存限制 65536 KB

题目描述

Mays王国的女王大人每天过着自由自在的生活,她最大的乐趣就是给邻国的帅气王子写信。而负责给她送信的就是皇家小妹妹快递公司。
今天负责给女王大人送信的是一个新来的小妹妹,她非常好奇女王大人的信,于是悄悄的把它拆开来看了!但是机智的女王大人早就想到了会有这种情况发生,她和邻国帅气王子的信都是加密过的~
小妹妹研究了一路,她感觉,里面重复比较多的内容应该是有用信息。为了安慰自己的智商,小妹妹希望找到信的一个最长连续的子串,这个子串出现2次或以上。为了能找到的子串尽可能长,小妹妹认为即便出现的2次有一部分重叠也是可以的。

输入格式

输入第一行为数据组数T(T<=10),每组一行字符串str,str中只包含小写字母,且长度不超过2000。

 

输出格式

每组答案输出一行。

输入样例

2
aabbaabbaa
abcde

输出样例

6
0
2000的长度,可以用暴力+剪枝做出来。
用后缀数组+高度数组可以高效地计算,甚至可以完成100000的长度。

注: 计算后缀数组时,用倍增的方法,利用长度为k的子串的顺序,高效的计算出长度为2*k的子串的顺序。
计算高度数组时,用类似尺取法的思想,因为后缀数组相邻的两个后缀的LCP一定是最大的,而已知i和k的lcp长度,i+1与k+1的lcp至少为原lcp-1,所以只需扫描之后的h。

代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;
int n,k;
int ranks[10000],tmp[10000],rankss[10000];
bool compare_sa(int i,int j)
{
    if(ranks[i]!=ranks[j])
        return ranks[i]<ranks[j];
    else
    {
        int ri=i+k<=n?ranks[i+k]:-1;
        int rj=j+k<=n?ranks[j+k]:-1;
        return ri<rj;
    }
}
void construct_sa(char* s,int *sa)
{
    n=strlen(s);
    for(int i=0;i<=n;i++)  //取0~n,i=n时表示空串 
    {
        sa[i]=i;			
        ranks[i]=i<n?s[i]:-1;	 //第一轮,直接取字符的ASSIC码值作为rank值 
    }
    for(k=1;k<=n;k*=2)
    {
        sort(sa,sa+n+1,compare_sa);
        tmp[sa[0]]=0;
        for(int i=1;i<=n;i++)//用tmp[]临时储存子串的新rank 
        {
            tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0);
        }
        for(int i=0;i<=n;i++)
            ranks[i]=tmp[i];
    }
}
void construct_lcp(char* s,int *sa,int *lcp)
{
    n=strlen(s);
    for(int i=0;i<=n;i++)
        rankss[sa[i]]=i;
    int h=0;
    lcp[0]=0;
    for(int i=0;i<n;i++)
    {
        int j=sa[rankss[i]-1];//lcp计算的是,当前子串与前一后缀的LCP 
        if(h>0)
            h--; //先将h减一 
        for(;j+h<n&&i+h<n;h++)
        {
            if(s[j+h]!=s[i+h])
                break;
        }
        lcp[rankss[i]-1]=h;
    }
}
int main()
{
    int t,i,sa[3000],lcp[3000];
    char s[3000];
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",s);
        construct_sa(s,sa);
        construct_lcp(s,sa,lcp);
        int maxn=0;
        for(i=0;i<n;i++)
        {
            maxn=max(maxn,lcp[i]);
        }
        printf("%d\n",maxn);
    }
    return 0;
}


时间限制 1000 ms  内存限制 65536 KB

题目描述

大家都知道,学校里有很多路在修,修路需要砖块。这一天,Mr.F来到集训队,找学妹去帮忙搬砖块。善良的学长们不忍心让学妹劳动,就争先恐后的帮助学妹搬砖。于是聪明的学妹说,我出一个题,谁答出来谁就能帮我搬砖。
把学校要铺的地面看成是n*m的方格,每一块砖的大小是1*2,学妹想知道有多少种方法可以把这块地铺满。注意地上有可能会有花花草草,有爱心的学妹不忍心砖块把它们压死,所以这些点是不可以铺砖块的。

输入格式

输入多组数据,数据组数不超过20组。每组第一行为三个整数n, m, k,(1<=n, m<=10),k<=n*m,分别代表地面的长宽,以及花花草草的数量。接下来k行,每行一组x,y,表示花花草草的坐标。详细方向见样例。

输出格式

每组输出一个数,即最后的方案数。由于输出会很大,请输出答案mod 1000000007(10^9+7)。

输入样例

3 1 1
2 0

3 1 0

输出样例

1
0

hint:
第一组样例所示地面为:
.
.
*
其中‘.‘表示空地,’*‘表示花花草草,一种方案可以铺满。
 轮廓线dp:

如图所示蓝色区域为完全覆盖区域,白色区域为完全未被覆盖区域,灰色及黄色为交界地带,即轮廓线。
其中黄色为当前处理的点。利用状态压缩,用0~1<<m-1枚举交界地带的覆盖情况,然后推到下一点各种覆盖情况的方案数。
如果当前点铺已覆盖,就不需要再铺砖;如果未覆盖,则考虑横铺和竖铺两种情况。

代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;
int n,m,k;
long long  a[20][20],dp[5][1<<15];
int main()
{
    int i,j,k,x,y,cur,used;
    while(scanf("%d %d %d",&n,&m,&k)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        for(i=0;i<k;i++)
        {
            scanf("%d %d",&x,&y);
            a[x][y]=1;
        }
        dp[0][0]=1;
        cur=0;
        for(i=0;i<n;i++)
        {
            for(j=0;j<m;j++)
            {
                memset(dp[1-cur],0,sizeof(dp[1-cur]));
                for(used=0;used<1<<m;used++)
                {
                    if((used>>j&1)||a[i][j])
                        dp[1-cur][used&~(1<<j)]=(dp[1-cur][used&~(1<<j)]+dp[cur][used])%1000000007;
                    else
                    {
                        if(j+1<m&&!(used>>(j+1)&1)&&!a[i][j+1])
                            dp[1-cur][used|1<<(j+1)]=(dp[1-cur][used|1<<(j+1)]+dp[cur][used])%1000000007;
                        if(i+1<n&&!a[i+1][j])
                            dp[1-cur][used|1<<j]=(dp[1-cur][used|1<<j]+dp[cur][used])%1000000007;
                    }
                }
                cur^=1;
            }
        }
        printf("%lld\n",dp[cur][0]);
    }
    return 0;
}


时间限制 1000 ms  内存限制 65536 KB

题目描述

Mays王国的女王大人每天过着自由自在的生活,她最大的乐趣就是给邻国的帅气王子写信。而负责给她送信的就是皇家小妹妹快递公司。
Mays王国由n个地区组成,一些双向的道路连接着这些地区。
最近,女王大人听说了有叔叔设置路障为难小妹妹,于是下令清理了所有的叔叔,并且封锁了一些道路,在保证所有地区相互可达的情况下使得Mays王国的路最少,这样,Mays王国就变成了一颗树的形状,这样小妹妹们就再也不会迷路啦。女王大人还播下了很多很多经费,小妹妹快递公司的董事长决定重新盖一栋小妹妹大楼。
为了选择一个好的区域来盖大楼,董事长希望它给公司带来的压力是最小的,给公司带来的压力就是小妹妹们出发时,同一方向上最多的小妹妹的数量。每送一次快递,公司会派出n-1个小妹妹给其他n-1个区域送快递。
无聊的beegerous听到了这个消息之后很难过,满怀恶意的他对着地图删掉了Mays王国的一些区域,于是小妹妹公司得到一个新的压力值和区域数目值,小妹妹们发现了一个规律:新的压力值不小于新的区域数目的一半。
比如原来的Mays王国有五个区域,四条道路分别连接区域1,2,区域1,3,区域2,4,区域3,5,那么小妹妹公司的新址就是区域1,压力值为2,beegerous可以通过去掉区域3和5,使得新的压力值为2,而新区域数的一半为1.5。
现在请你计算有多少种删除的方法。
注意,热爱Mays王国的beegerous不会在删完某些区域之后使得Mays王国不连通。

输入格式

输入第一行为数据组数T(T<=10),接下来每组输入第一行为区域数n(1<=n<=200),接下来n-1行每行两个整数u,v(1<=u,v<=n),表示一条道路连接区域u,v。输入保证小妹妹公司的新址是唯一的。

输出格式

输出一个数,方案数对10007取模。

输入样例

4
1
3
1 2
1 3
4
1 2
1 3
1 4
5
1 2
1 3
1 4
4 5

输出样例

0
2
3
6
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值