10.3离线赛

来源:互联网 发布:乐乎公寓 百子湾店 编辑:程序博客网 时间:2024/06/05 03:12

预分:200 实分:175

写书
应得:100 实得:100

题意:计算1到n的数字出现个数

数据:对于100%,n∈[1,1e9];

小学奥数题,没有必要多想,一位数两位数三位数直接计算即可。

LR棋盘
应得:40 实得:35

题意:读入一个字符串,在串上有LR两种标记,L只能向左走,R只能向右走,每次只能走一个L或R,只能向没东西的格子走,问有多少种走法。最后对1e9+7取模

数据:对于40%,len∈[1,10],棋子数∈[1,5];
对于100%,len属于[1,20000],棋子数∈ [1,2000]

看到题后觉得要么是dp,要么是排列组合,因为要取模,不可能有暴力求解。
dp[i]定义为从1到i空有几种方法,但是这样就很难计算,因为LR可以移动到i的外面。
从另一个角度思考,因为LR放在一起时会变成一个独立的区间,那么可以单独处理最后相加,这样dp[i]就是1到第i个符号所有的方案数,只要再记录一下每个LR的区间即可

#include<bits/stdc++.h>#define Mod 1000000007#define M 200005using namespace std;char str[M];int L[M],R[M],dp[M];int main(){    scanf("%s",str);    int n=strlen(str);    int cnt=0;    for(int i=0;i<n;i++)        if(str[i]=='L')cnt++,L[cnt]=0,R[cnt]=i;        else if(str[i]=='R')cnt++,L[cnt]=i,R[cnt]=n-1;    //记录LR的位置    dp[0]=1;    for(int i=0;i<n;i++)        for(int j=cnt;j>=1;j--)            if(L[j]<=i&&i<=R[j])//若满足i在这段区间内就加                dp[j]+=dp[j-1],dp[j]%=Mod;    printf("%d\n",dp[cnt]);    return 0;}

这个和之前有一道基因补全很像,那道是往一个序列里放数,这道题也可以这么想,只要保证他们的相对位置不变即可。

道路评价
应得:60 实得:40

题意:在一棵树上求任意两点见路径上最大值减最小值的差的和

数据:对于60%,n∈[1,5000];
对于100%,n属于[1,100000],边权∈[1,100000]

第一点,边权这么大,肯定要用long long才行
第二点,5000^2可以开一下,及吧每一个点都向其他点走一遍,dfs里多传最大最小值就行了,这样应该60,我却只有40。然后我把max,min改成if后就60了。
第三点,考试时想到一个方法,我们只需要求每一条边作为最大路径,最小路径出现的次数即可,但是朴素的方法求很慢,每一条边求一次就要n,最后依旧是n^2,没有区别。正解也是这样写的,但是再求边的使用数量上用并查集写。先将边排个序,从小到大,每次先求最大的,然后把这条边两边的点合并成一个点计算。正确性在于排序后下次访问的边肯定比之前的边要短,那么可以直接使用。这是对于求最小边,最大边反一下即可。
以下是图示
原图
合并一条边后

#include<bits/stdc++.h>#define M 100005using namespace std;struct node{int x,y,z;}A[M];bool cmp(node x,node y){return x.z>y.z;}int fa[M],cnt[M];//fa[]是父亲节点,cnt是求合并后这个点代表着几个点int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}int main(){    int n;    scanf("%d",&n);    for(int i=1;i<n;i++)scanf("%d%d%d",&A[i].x,&A[i].y,&A[i].z);    sort(A+1,A+n,cmp);//按边权从小到大    for(int i=1;i<=n;i++)fa[i]=i,cnt[i]=1;//初始化    long long Max=0,Min=0;    for(int i=1;i<n;i++){        int x=Find(A[i].x),y=Find(A[i].y);        Min+=(1LL)*A[i].z*cnt[x]*cnt[y];        fa[x]=y;        cnt[y]+=cnt[x];    }    for(int i=1;i<=n;i++)fa[i]=i,cnt[i]=1;    for(int i=n-1;i>=1;i--){//循环反过来,避免一次排序        int x=Find(A[i].x),y=Find(A[i].y);        Max+=(1LL)*A[i].z*cnt[x]*cnt[y];        fa[x]=y;        cnt[y]+=cnt[x];    }    printf("%lld\n",Max-Min);    return 0;}

这样就可以n logn完成

原创粉丝点击