1. 程式人生 > >每日一道模板題 - 持續更新中

每日一道模板題 - 持續更新中

1008 - KMP

LOJ 剪花布條

簡化版題意

【描述】 
給定 2個字串 S,T,判斷 S中包含多少不重疊的子串=T 
【輸入  】 
多組資料 
讀到當個字元’#’表示結束,注意是單個,如果有字串開頭有#,不意味結束 
每組資料僅有一行,為由空格分開的 S和 T。 
【輸出】 
對於每組資料,輸出一行整數,表示答案 
【輸入樣例】 
abcde a3 
aaaaaa aa 

【輸出樣例】 


【資料規模】 
對於全部資料,字串長度  ≤1000   

 

程式碼

#include<bits/stdc++.h>
#define N 1009
using namespace std;
char a[N],b[N];
int lena,lenb,nxt[N];
int main(){
	while(1){
		memset(nxt,0,sizeof(nxt));
		int i=0,j=0,k=-1;
		scanf("%s",a+1);//自動忽略空格 
		lena=strlen(a+1);
		if(lena==1&&a[1]=='#') break;//這兩個判斷一個都不能少 
		scanf("%s",b+1);
		lenb=strlen(b+1);
		int ans=0;
		nxt[1]=0;
		for(int i=2,j=0;i<=lenb;++i){
			while(j&&b[i]!=b[j+1]) j=nxt[j];
			if(b[i]==b[j+1]) j++;
			nxt[i]=j;
		}
		j=0;
		for(int i=1;i<=lena;++i)
		{
			while(j&&a[i]!=b[j+1]) j=nxt[j];
			if(a[i]==b[j+1]) j++;
			if(j==lenb){
				ans++;
				j=0;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

 

1009最短路之dijkstra+堆優化

 

【描述】 
N個點 m條邊的無向圖,計算從起點 S到 T的最短路,保證連通 
【輸入】 
第一行 N(<=2500),M(<=1000),S,T 
接下來 M行,每行 3個數字 U,V,W表示 u和v之間有路徑長度為 W(1<=Wi<=1000) 
【輸出】 
1個整數 
【樣例】 
7 11 5 4 
2 4 2 
1 4 3 
7 2 2 
3 4 3 
5 7 5 
7 3 3 
6 1 1 
6 3 4 
2 4 3 
5 6 3 
7 2 1 
【輸出樣例】 

 

程式碼

#include<bits/stdc++.h>
#define N 3000
#define M 40009
#define in read()
using namespace std;
inline int read(){
    char ch;int f=1,res=0;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    while(ch>='0'&&ch<='9'){
        res=(res<<3)+(res<<1)+ch-'0';
        ch=getchar();
    }
    return f==1?res:-res;
}
int nxt[M],to[M],head[N],w[M],cnt=0;//陣列大小打錯了…… 
void add(int x,int y,int z){
    nxt[++cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
int n,m,s,t,d[N];
priority_queue<pair<int ,int> > q;
void di(int x){
    memset(d,0x3f3f3f3f,sizeof(d));
    d[x]=0;
    q.push(make_pair(x,0));
    while(!q.empty()){
        int u=q.top().first;q.pop();
        for(int e=head[u];e;e=nxt[e]){
            int v=to[e];
            if(w[e]+d[u]<d[v]){
                d[v]=d[u]+w[e];
                q.push(make_pair(v,-d[v]));
            }
        }
    }
}
int main(){
    n=in;m=in;s=in;t=in;
    int i,j,k,u,v,z;
    for(int i=1;i<=m;++i){
        u=in;v=in;z=in;
        add(u,v,z);add(v,u,z);
    }
    di(s);
    printf("%d",d[t]);
    return 0;
}
 

 

1010樹狀陣列求逆序對

 

題面

給定一個序列

求這個序列中逆序對的個數

要求:必須使用樹狀陣列

 

分析

雖然就是這麼簡單一個模板,但我還是寫WA了

為什麼呢????(不服氣╭(╯^╰)╮)

因為啊要先插入,再查詢,要清楚現在查詢的是小於等於當前數的個數(包括了自己,所以要把自己加進去,才能減掉自己)

 

程式碼

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int lowbits(int x){
    return x&(-x);
}
ll tr[100009];
int n,a;
void insert(ll x){
    while(x<=100009){
        tr[x]++;
        x+=lowbits(x);
    }
}
int ask(int x){
    ll res=0;
    while(x){
        res+=tr[x];
        x-=lowbits(x);
    }
    return res;
}
int main(){
    memset(tr,0,sizeof(tr));
    scanf("%d",&n);
    int i,j,k;
    ll sum=0;
    for(i=1;i<=n;++i){
        scanf("%d",&a);insert(a);//先插入後查詢 
        sum+=i-ask(a);
    }    
    cout<<sum;
    return 0;

 

 

1011 - 點的距離【lca】

題意:

給定一棵 n 個點的樹,Q 個詢問,每次詢問點 x 到點 y兩點之間的距離。

 

分析:

沒什麼好分析的……

 

程式碼:

#include<bits/stdc++.h>
#define N 100009
using namespace std;
int n,q;
int nxt[2*N],to[2*N],head[N],w[N*2],cnt=0;
void add(int x,int y,int z){
    nxt[++cnt]=head[x];head[x]=cnt;
    to[cnt]=y;w[cnt]=z;
}
int fa[N][25],d[N],dep[N];
void dfs(int u,int fu){
    fa[u][0]=fu;
    for(int e=head[u];e;e=nxt[e]){
        int v=to[e];
        if(v==fu) continue;
        d[v]=d[u]+1;dep[v]=dep[u]+1;
        dfs(v,u);
    }
}
int getlca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;--i) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;--i)
    {
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];y=fa[y][i];
        }
    }
    return  fa[x][0];
}
int main(){
    scanf("%d",&n);
    int i,j,k,x,y;
    for(i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        add(x,y,1);
    }
    d[1]=0;dep[1]=1;
    dfs(1,0);
    for(j=1;j<=20;++j)
        for(i=1;i<=n;++i)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    scanf("%d",&q);
    for(i=1;i<=q;++i){
        scanf("%d%d",&x,&y);
        int lca=getlca(x,y);
        printf("%d\n",d[x]+d[y]-d[lca]*2);
    }
    return 0;
}


 
1012 -  trie樹

描述

給定N個字串S1,S2...SN,接下來進行M次詢問,每次詢問給定一個字串T,求S1~SN中有多少個字串是T的字首。輸入字串的總長度不超過10^6,僅包含小寫字母。

輸入格式

第一行兩個整數N,M。接下來N行每行一個字串Si。接下來M行每行一個字串表示詢問。

輸出格式

對於每個詢問,輸出一個整數表示答案

樣例輸入

3 2
ab
bc
abc
abc
efg
樣例輸出

2
0
 

分析

頹啊……連trie樹都寫WA了,我還能做什麼……仰天長嘆,但別沮喪,知道錯了,才好改正,加油女孩,你可以的

trie樹板題,這倒是沒什麼好分析的

 

程式碼

#include<bits/stdc++.h>
using namespace std;
int n,m,trie[9000009][30],cnt[9000009],tot=1;
char st[1000009];
void insert(){
    int len=strlen(st),pos=1;
    for(int i=0;i<len;++i){
        int c=st[i]-'a';
        if(!trie[pos][c])    trie[pos][c]=++tot;
        pos=trie[pos][c];
    }
    cnt[pos]++;//這個地方不是tot,並不是每次都會新建節點好吧 
}
int query(){
    int res=0,len=strlen(st),pos=1;
    for(int i=0;i<len;++i){
        int c=st[i]-'a';
        if(trie[pos][c]) res+=cnt[trie[pos][c]],pos=trie[pos][c];
        else break;
    }
    return res;
}
int main(){
    scanf("%d%d",&n,&m);
    int i,j;
    for(i=1;i<=n;++i){
        scanf("%s",st);
        insert();
    }
    for(i=1;i<=m;++i){
        scanf("%s",st);
        printf("%d\n",query());
    }
    return 0;
}


 
1013 - ST表

分析

就是無分析

 

程式碼

就是Ac的程式碼

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100009
#define in read()
using namespace std;
inline int read(){
    char ch;int f=1,res=0;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    while(ch>='0'&&ch<='9'){
        res=(res<<3)+(res<<1)+ch-'0';
        ch=getchar();
    }
    return f==1?res:-res;
}
int n,m,f[N][20],Log[N];
void init(){
    for(int j=1;j<=Log[n];++j)
        for(int i=1;i+(1<<j-1)<=n;++i)
            f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
int a[N];
int main(){
    Log[1]=0;
    for(int i=2;i<=100000;++i)
        Log[i]=Log[i/2]+1;
    n=in;m=in;
    for(int i=1;i<=n;++i)
    {
        a[i]=in;
        f[i][0]=a[i];
    }
    init();
    for(int i=1;i<=m;++i){
        int l=in;int r=in;
        int k=Log[r-l+1];
        printf("%d\n",max(f[l][k],f[r-(1<<k)+1][k]));
    }
    return 0;
}


1014 - 線段樹

涼涼涼了……昨天光調dp去了,模板都沒打

今天補一下補一下補一下(假裝沒有看到)

好像有點水……不過沒關係

給出n個數(1 < = n < = 100000 ),並且初始化所有數字都為0.接下來m次操作,( 1<= m < = 100000 ) 操作有以下兩種:

1: C X K 把第X個數的值增加A(A可正可負)

2: P X Y 就是詢問 第X個數至 第Y個數 的所有數的和。

程式碼

#include<bits/stdc++.h>
#define lc (k<<1)
#define rc (k<<1)|1
using namespace std;
int n,m,sum[500009];
void modify(int k,int l,int r,int pos,int x){
	if(l==r){
		sum[k]+=x;
		return ;
	}
	int mid=l+r>>1;
	if(pos<=mid) modify(lc,l,mid,pos,x);
	else modify(rc,mid+1,r,pos,x);
	sum[k]=sum[lc]+sum[rc];
}
int query(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y) return sum[k];
	int res=0;
	int mid=l+r>>1;
	if(x<=mid) res+=query(lc,l,mid,x,y);
	if(y>mid) res+=query(rc,mid+1,r,x,y);
	return res; 
}
int main(){
	scanf("%d%d",&n,&m);
	int i,j,k,x,y;char ch[5];
	memset(sum,0,sizeof(sum));
	for(i=1;i<=m;++i){
		scanf("%s",ch);
		scanf("%d%d",&x,&y);
		if(ch[0]=='C')	modify(1,1,n,x,y);
		else	printf("%d\n",query(1,1,n,x,y));
	}
	return 0;
}

 

 

1015 - 快速冪&快速乘 

貼個部落格

今天還複習了一下Miller_Rabin演算法

順帶去看了一下Pollar_rho(沒看懂)

然後dzyo告訴我這個東西,我可能還沒辦法掌握……就又涼了

我發現我今天選的兩個演算法居然都遠遠超過我自身的可學水平,有點小難過(;′⌒`)

 

1016 - trie樹

 

電話 
XXX 交際甚廣,他的厚厚的電話簿裡存滿了電話號碼。每天,XXX 都會打電話
給他的 一個朋友以約他出來玩。然而,令他十分惱火的是,有時會出現如下情
況:他有兩個朋友, 其中一個電話號碼為  123456,另一個為  12345。當他給 
123456 打電話時,按下  12345 後可 能直接就接通到另一個朋友  12345 那裡
去了。這時可就麻煩了!12345 會以為XXX 是找 他玩,而  XXX就不能照自
己的計劃找  123456 玩了。現在,XXX 想請你檢查一下他的電 話簿是否存在
這種隱患。 換句話說, 你需要判斷其中是否有一個電話號碼是另一個電話號碼 
的字首。 

 

程式碼

#include<bits/stdc++.h>
#define N 500009
using namespace std;
int T,tot=0,cnt[N],trie[N][15];//資料範圍 
char st[15];
bool insert(){
	int len=strlen(st),i,pos=0,flag=0;
	for(i=0;i<len;++i){
		int c=st[i]-'0';
		if(!trie[pos][c]) {
			memset(trie[++tot],0,sizeof(trie[tot]));///
			trie[pos][c]=tot;
		}
		//else if(cnt[pos]) flag=1;不能這樣寫,尾巴上那個節點不會被納入計算 
		pos=trie[pos][c];
		if(cnt[pos]) flag=1;
	}
	cnt[pos]++;
	for(i=0;i<=9;++i) if(trie[pos][i]) flag=1;
	return flag;

}
int n;
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		tot=0;memset(cnt,0,sizeof(cnt));
		memset(trie[0],0,sizeof(trie[0]));///也可以這樣清空哦~ 
		int i,j,k,flag=0;
		for(i=1;i<=n;++i)
		{
			scanf("%s",st);
			if(!flag)flag =insert();		
		}	
		if(flag)	printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

 

1017 - 無向圖Tarjan邊雙連通

 

結論:將一個有橋圖轉成邊雙連通的圖,最少需要增加(leaf+1)/ 2 條邊

補充結論:將一個有向圖轉成強聯通圖,最少需要增加max(入度為0的個數,出度為0的個數)

其中 leaf 指將原圖縮點後得到的葉子節點的個數 

 

程式碼

#include<bits/stdc++.h>
#define in read()
#define N 5005
#define M 30000
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,m;
int du[N],dfn[N],low[N],cnt=0,be[N],num=0;
bool insta[N];
stack<int > S;
int nxt[M],head[N],to[M],ecnt=1,from[M];
void add(int x,int y){
	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;from[ecnt]=x;
}
void tarjan(int u,int from){
	S.push(u);insta[u]=1;
	dfn[u]=low[u]=++cnt;
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(!dfn[v]){
			tarjan(v,e);
			low[u]=min(low[u],low[v]);
		}
		else if(e!=(from^1)&&insta[v]) low[u]=min(low[u],dfn[v]);
	}
	int x; 
	if(low[u]==dfn[u]){
		++num;
		do{
			x=S.top();S.pop();
			be[x]=num;insta[x]=0;
		}while(x!=u);
		
	}
}
int main(){
	n=in;m=in;
	int i,j,k,u,v;
	for(i=1;i<=m;++i){
		u=in;v=in;
		add(u,v);add(v,u);
	}
	for(i=1;i<=n;++i)
		if(!dfn[i]) tarjan(i,0);
	for(i=2;i<=ecnt;i++){
		int u=from[i],v=to[i];
		if(be[u]!=be[v]){
			du[be[v]]++;
		}
	}
	int tot=0;
	if(num==1) {
		printf("0");
		return 0;
	}
	for(i=1;i<=num;++i)
		if(du[i]==1) tot++;
	if(tot&1) tot++;
	cout<<tot/2;
	return 0;
}

 

 

1018 - 擴充套件歐幾里得

吃蛋糕

描述

Beny 想要用蛋糕填飽肚子。Beny 一共想吃體積為 c 的蛋糕,他發現有兩種蛋糕可以吃,一 種體積為 a,一種體積為 b,但兩種蛋糕各有特色。Beny 想知道他一共有多少種不同吃法, 使得他恰好可以填飽肚子。

輸入

第一行一個 t

接下來 t 行,每行三個正整數 a,b,

輸出

對於每個 a,b,c,輸出一個整數表示有幾種不同吃法

 

分析

題是好題,就是資料有鍋吧……全機房面向資料程式設計???有意思

還是對擴充套件歐幾里得掌握不牢啊哎

好好寫一篇部落格分析一下