10.3離線賽

預分: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 200005
using 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 100005
using 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完成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章