1008 - KMP
简化版题意
【描述】
给定 2个字符串 S,T,判断 S中包含多少不重叠的子串=T
【输入 】
多组数据
读到当个字符’#’表示结束,注意是单个,如果有字符串开头有#,不意味结束
每组数据仅有一行,为由空格分开的 S和 T。
【输出】
对于每组数据,输出一行整数,表示答案
【输入样例】
abcde a3
aaaaaa aa
#
【输出样例】
0
3
【数据规模】
对于全部数据,字符串长度 ≤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
【输出样例】
7
代码
#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,输出一个整数表示有几种不同吃法
分析
题是好题,就是数据有锅吧……全机房面向数据编程???有意思
还是对扩展欧几里得掌握不牢啊哎
1019 - 并查集
map,字符串读入