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