每日一道模板题 - 持续更新中

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/82973186

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,输出一个整数表示有几种不同吃法

分析

题是好题,就是数据有锅吧……全机房面向数据编程???有意思

还是对扩展欧几里得掌握不牢啊哎

好好写一篇博客分析一下

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/82973186