暑期总结

暑期总结

  • 学了什么
    • 数学:扩展欧几里得,欧拉函数,数论三大定理
    • 数学:排列组合
    • 数学:矩阵
    • 数学:期望
    • 字符串哈希,字典树,最小表示
    • 树论:树形dp,树上差分
    • 状压dp
    • st表
    • 无向tarjan
      • 二分图
    • 基环树
    • 差分约束
  • 自学
    • 并查集提高
    • 线段树的某些题型
    • ~~分块·~~
  • 下阶段任务
    • 博弈论
    • ==分块==~~好好看~~
    • 网络流
    • ~~高斯消元(有什么用呢)~~
    • 树链剖分
    • ~~二分查找树~~

学了什么

数学:扩展欧几里得,欧拉函数,数论三大定理

线性筛质数

void prime()
{
	for(int i=2;i<=n;++i)
	{
		if(!f[i]) p[++cnt]=i;
		for(int j=1;j<=cnt&&p[j]*i<=n;++j)
		{
			f[p[j]*i]=1;if(i%p[j]==0) break;
		}
	}
}

线性同余

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a,b;
int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1,y=0;return a;
	}
	int r=exgcd(b,a%b,x,y);
	int tem=x;
	x=y,y=tem-a/b*y;
	return r;
}
int line_equation(int a,int b,int c,int &x,int &y)
{
	int d=exgcd(a,b,x,y);
	if(c%d) return 0;
	int k=c/d;
	x=x*k;y=y*k;
	return 1;
}
int tong_yu(int a,int b,int m)
{
	int x,y,x0,i;
	int d=exgcd(a,m,x,y);
	if(b%d) return 0;
	x0=b/d*x;
	int ans=x0,s=m/d;
	ans=(ans%s+s)%s;
	return ans;
}
main()
{
	scanf("%lld%lld",&a,&b);//ax=1(mod b)
	cout<<tong_yu(a,1,b);
}

乘法逆元

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,p;
int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;return a;
    }
    int r=exgcd(b,a%b,x,y);
    int tem=x;x=y,y=tem-a/b*y;
    return r;
}
int tongyu_inv(int a,int m)
{
    int x,y;
    int d=exgcd(a,m,x,y);
    if(d!=1) return 0;
    return (x%m+m)%m;
}
main()
{
    scanf("%lld%lld",&n,&p);
    for(register int i=1;i<=n;++i)
    {
        printf("%lld\n",tongyu_inv(i,p));
    }
}

玄学算法
签到题
仪仗队
同余方程
乘法逆元
尝试一下
按钮~~(据说很水)~~.

数学:排列组合

lucas
卡特兰
打个表

如果n=1 1
如果n=2 2
如果n=3 5
如果n=4 14
如果n=5 42
如果n=6 132
如果n=7 429
如果n=8 1430
如果n=9 4862
如果n=10 16796
如果n=11 58786
如果n=12 208012
如果n=13 742900
如果n=14 2674440
如果n=15 9694845
如果n=16 35357670
如果n=17 129644790
如果n=18 477638700

康托展开,容斥
卢卡斯.
小猫.
尝试一下
硬币购物.

数学:矩阵

加速递推

#include<bits/stdc++.h>
#define inf 1000000007 
using namespace std;
long long a[5][5],b[5][5],f[5],c[5];
long long n,m,i,j,k,l,T;
void kuai();
int main()
{
    scanf("%d",&T);
    for(i = 1;i <= T; i++)
    {
    memset(a,0,sizeof(a));
    memset(f,0,sizeof(f));
    scanf("%d",&n);
    a[1][2] = a[2][3] = a[3][4] = 1;
    a[4][1] = a[4][2] = a[4][3] = 1;
    f[1] = f[2] = f[3] = 1;
    f[4] = 2;    
    if(n <= 3 )
      {
        cout<<1<<endl;
        continue; 
      } 
        n -= 4; 
      kuai();
      cout<<f[4]<<endl;
    }
    return 0;
}
void kuai()
{
    int i,j,k;
    while(n > 0)
    {
        if(n % 2 == 1)
        {
            memset(c,0,sizeof(c));
            for(i = 1; i <= 4; i++)
              for(j = 1;j <= 1; j++)
                for(k = 1;k <= 4; k++)
                  c[i]  = (c[i] + a[i][k]*f[k])%inf;
            for(i = 1;i <= 4; i++)
              f[i] = c[i];          
        }
        memset(b,0,sizeof(b));
        for(i = 1;i <= 4; i++)
          for(j = 1;j <= 4; j++)
            for(k = 1;k <= 4; k++)
                b[i][j] = (b[i][j] + a[i][k]*a[k][j])%inf;
        for(i = 1;i <= 4; i++)
          for(j = 1;j <= 4; j++)        
            a[i][j] = b[i][j];    
        n /= 2;
    }
}

矩阵快速幂

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
int n,k;
struct myjuzhen{
	int a[102][102];
};
inline int read()//读入优化 
{
    char c=getchar();int x=0,f=1;
    while(!isdigit(c)) {if(c == '-')f=-1;c=getchar();}
    do{x=x*10+(c^48);c=getchar();}while(isdigit(c));
    return x*f;
}
myjuzhen mul(myjuzhen a,myjuzhen b)
{
	myjuzhen ans;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			ans.a[i][j]=0;
			for(int k=1;k<=n;++k)
			{
				ans.a[i][j]+=a.a[i][k]*b.a[k][j];
				ans.a[i][j]%=p;
			}
		}
	}return ans;
}
myjuzhen pow(myjuzhen a,int b)
{
	myjuzhen r,base=a;
	for(int i=1;i<=n;++i) r.a[i][i]=1;
	while(b)
	{
		if(b&1) r=mul(r,base);
		base=mul(base,base);
		b>>=1;
	}
	return r;
}
main()
{
	scanf("%lld%lld",&n,&k);
	myjuzhen A,ans;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			A.a[i][j]=read();
		}
	}
	ans=pow(A,k);
	//cout<
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			printf("%lld ",ans.a[i][j]);
		}
		printf("\n");
	}
}

某年月日水题
矩阵快速幂模板

数学:期望

全概率公式
贝叶斯公式
绿豆蛙的归宿
小魔女帕琪

字符串哈希,字典树,最小表示

字符串哈希(set,map)

#include<bits/stdc++.h>
using namespace std;
#define int long long
map<int,int>a1,a2;
int n,ans;
const int b=131,p1=1411230001;
char c[1502];
main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",c);int len=strlen(c),st1=0;
        for(int j=0;j<=len;++j)
        {
            st1=(st1*b+c[j]-'0')%p1;
        }
        if(a1.find(st1)!=a1.end())
        {
            
        }
        else{
            ++ans,a1[st1]=1;
        }
    }
    printf("%d\n",ans);	
}

字典树
模板是某题ac码

#include<bits/stdc++.h>
using namespace std;
#define int long long
map<int,int>a1,a2;
int n,ans;
const int b=131,p1=1411230001;
char c[1502];
main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",c);int len=strlen(c),st1=0;
        for(int j=0;j<=len;++j)
        {
            st1=(st1*b+c[j]-'0')%p1;
        }
        if(a1.find(st1)!=a1.end())
        {
            
        }
        else{
            ++ans,a1[st1]=1;
        }
    }
    printf("%d\n",ans);	
}

最小表示
模板仍是某题ac码

#include<bits/stdc++.h>
using namespace std;
int n,a[2*300010],ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        int x;scanf("%d",&x);a[i]=a[i+n]=x;
    }
    int i=1,j=2,k;
    while(i<=n&&j<=n)
    {
        for(k=0;k<n&&a[i+k]==a[j+k];k++);
        if(k==n) break;
        if(a[i+k]>a[j+k])
        {
            i=i+k+1;if(i==j) i++;
        }
        else{
            j=j+k+1;if(i==j) j++;
        }
    }ans=min(i,j);
    for(int l=1;l<=n;++l)
    {
        printf("%d ",a[l+ans-1]);
    }
}

前缀统计
the xor largest pair
The xor-longest Path==(只有我很烦poj吗)==
工艺

树论:树形dp,树上差分

这里面的题目,都很模板— SunX

拿一道简单的蓝题做模板吧
最大流Max Flow

左转复习lca去
点:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,cnt,h[50002],dep[50002],fa[50002][18],f[50002];
struct edg{
	int y,next;
}edg[100003];
inline int mymax(int x,int y)
{
	if(x>y) return x;return y;
}
inline void add(int x,int y)
{
	edg[++cnt].y=y,edg[cnt].next=h[x],h[x]=cnt;
}
inline void maketree(int x,int fat)
{
	for(int i=h[x];i;i=edg[i].next)
	{
		int y=edg[i].y;if(y==fat) continue;
		dep[y]=dep[x]+1;fa[y][0]=x;
		maketree(y,x);
	}
}
void bz()
{
	for(int j=1;j<=17;++j)
	{
		for(int i=1;i<=n;++i)
		{
			fa[i][j]=fa[fa[i][j-1]][j-1];
		}
	}
}
inline int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	int d=dep[x]-dep[y];
	for(int i=0;i<=17;++i)
	{
		if((1<<i)&d) x=fa[x][i];
	}
	if(x==y) return x;
	for(int i=17;i>=0;--i)
	{
		if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	}
	return fa[x][0];
}
inline void work(int x,int y)
{
	int anc=lca(x,y);
	f[x]++,f[y]++,f[anc]--,f[fa[anc][0]]--;
}
void tongji(int x,int fa)
{
	for(int i=h[x];i;i=edg[i].next)
	{
		int y=edg[i].y;if(y==fa) continue;
		tongji(y,x);f[x]+=f[y];
	}
}
main()
{
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n-1;++i)
	{
		int x,y;scanf("%lld%lld",&x,&y);add(x,y),add(y,x);
	}
	maketree(1,0);bz();
	for(int i=1;i<=k;++i)
	{
		int x,y;scanf("%lld%lld",&x,&y);work(x,y);
	}
	tongji(1,0);int ans=0;
	for(int i=1;i<=n;++i)
	{
		ans=mymax(ans,f[i]);
	}printf("%lld\n",ans);
}

边的差分没有例题
d[u]+=val,d[v]+=val,d[a]-=2*val

天天爱跑步
据说放2018算水题???
先贴25分朴素代码做模板

#include<bits/stdc++.h>
using namespace std;
#define reg register
int n,m,w[3000000],h[3000000],cnt,fa[300000][23],dep[300000],f[300000],anc,ans[300000],ava1[300000],ava2[300000];
inline int read() 
{
    char c;int x=0,f=1;c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return f*x;
}
struct edg{
    int y,next;
}edg[3000000*2];
inline void add(int x,int y)
{
    edg[++cnt].y=y,edg[cnt].next=h[x],h[x]=cnt;
}
void maketree(int x,int fat)
{
    for(reg int i=h[x];i;i=edg[i].next)
    {
        int y=edg[i].y;if(y==fat) continue;
        dep[y]=dep[x]+1;fa[y][0]=x;maketree(y,x);
    }
}
void bz()
{
    for(reg int j=1;j<=22;++j) for(reg int i=1;i<=n;++i) fa[i][j]=fa[fa[i][j-1]][j-1];
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);int t=dep[x]-dep[y];
    for(reg int i=0;i<=22;++i) if((1<<i)&t) x=fa[x][i];if(x==y) return x;
    for(reg int i=22;i>=0;--i) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];return fa[x][0];
}
void dfs(int x,int fat,int lim,int tar1,int tar2)
{
    if(x==tar1) ava1[x]=1;if(x==tar2) ava2[x]=1;
    for(reg int i=h[x];i;i=edg[i].next)
    {
        int y=edg[i].y;if(y==fat) continue;if(dep[y]==lim&&(y!=tar1)&&(y!=tar2)) continue;
        dfs(y,x,lim,tar1,tar2);f[x]+=f[y];
        if(ava1[y]) ava1[x]=1;if(ava2[y]) ava2[x]=1;
    }
    if(f[x]&&ava1[x]&&dep[tar1]==w[x]+dep[x]) ++ans[x];
    else if(f[x]&&ava2[x]&&(dep[tar1]+dep[x]-2*dep[anc]==w[x])) ++ans[x];
}
void work(int x,int y)
{
    if(x==y)
    {
        if(w[x]==0) ++ans[x];
        return;
    }
    memset(f,0,sizeof(f)),memset(ava1,0,sizeof(ava1)),memset(ava2,0,sizeof(ava2));
    anc=lca(x,y);f[x]++,f[y]++,f[anc]--;f[fa[anc][0]]--;
    dfs(anc,fa[anc][0],max(dep[x],dep[y]),x,y);
}
int main()
{
    n=read(),m=read();
    for(reg int i=1;i<=n-1;++i)
    {
        int x=read(),y=read();add(x,y),add(y,x);
    }maketree(1,0);bz();
    for(reg int i=1;i<=n;++i)
    {
        w[i]=read();
    }
    for(reg int i=1;i<=m;++i)
    {
        int x=read(),y=read();work(x,y);
    }
    for(reg int i=1;i<=n;++i)
    {
        printf("%d ",ans[i]);
    }
}

然后就要想了:
1.我们dfs了多少次呢。。。
2.能不能一次完成呢。。。
3.怎么存关系?
出现与消失是在某一点上的信息能不能先存起来呢
vector
还是熟悉的配方,但它变了
只dfs一次
加O2更是飞起。。。毕竟stl

#include<vector>
#include<bits/stdc++.h>
using namespace std;
#define reg register
int n,m,w[3000010],h[3000011],cnt,fa[300001][23],dep[300011],ans[300011],anc;
vector<int> q1[300010],q2[3000010],p1[300011],p2[3000011];
int c1[300001*2],c2[300001*2],vis[300001];
inline int read() 
{
    char c;int x=0,f=1;c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return f*x;
}
struct edg{
    int y,next;
}edg[3000000*2];
inline void add(int x,int y)
{
    edg[++cnt].y=y,edg[cnt].next=h[x],h[x]=cnt;
}
void maketree(int x,int fat)
{
    for(reg int i=h[x];i;i=edg[i].next)
    {
        int y=edg[i].y;if(y==fat) continue;
        dep[y]=dep[x]+1;fa[y][0]=x;maketree(y,x);
    }
}
void bz()
{
    for(reg int j=1;j<=22;++j) for(reg int i=1;i<=n;++i) fa[i][j]=fa[fa[i][j-1]][j-1];
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);int t=dep[x]-dep[y];
    for(reg int i=0;i<=22;++i) if((1<<i)&t) x=fa[x][i];if(x==y) return x;
    for(reg int i=22;i>=0;--i) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];return fa[x][0];
}
void work(int x,int y)
{
    anc=lca(x,y);
    q1[x].push_back(dep[x]),p1[fa[anc][0]].push_back(dep[x]);
    q2[y].push_back(dep[x]-2*dep[anc]),p2[anc].push_back(dep[x]-2*dep[anc]);
}
void dfs(int x,int fa)
{
    int val1=c1[dep[x]+w[x]],val2=c2[w[x]-dep[x]+n];//这种情况可能下溢出orz
    for(int i=h[x];i;i=edg[i].next)
    {
        int y=edg[i].y;
        if(y==fa) continue;
        dfs(y,x);
    } 
    for(int i=0;i<q1[x].size();++i) c1[q1[x][i]]++;
    for(int i=0;i<p1[x].size();++i) c1[p1[x][i]]--;
    for(int i=0;i<q2[x].size();++i) c2[q2[x][i]+n]++;
    for(int i=0;i<p2[x].size();++i) c2[p2[x][i]+n]--;
    ans[x]+=c1[dep[x]+w[x]]-val1+c2[w[x]-dep[x]+n]-val2;return;
}
int main()
{
    n=read(),m=read();
    for(reg int i=1;i<=n-1;++i)
    {
        int x=read(),y=read();add(x,y),add(y,x);
    }maketree(1,0);bz();
    for(reg int i=1;i<=n;++i)
    {
        w[i]=read();
    }
    for(reg int i=1;i<=m;++i)
    {
        int x=read(),y=read();work(x,y);
    }
    dfs(1,0);
    for(reg int i=1;i<=n;++i)
    {
        printf("%d ",ans[i]);
    }
}

运输计划

状压dp

状压dp即状态压缩dp。
一般的dp往往着眼于整体,从中提取出两三个关键信息,并依此划分阶段使
得问题具备无后效性及最优子结构性质,然后依据已知信息对各个阶段进行
决策。然而有时候一般的状态难以满足无后效性,或者保存的信息不足以描
述完整的状态,许多元素的状态都直接影响到决策,都需要被考虑到。因此,
这时候将各个元素的状态看成一个集合,进行压缩存储,比如看成二进制的
数。这就是状态压缩。
我们常把以一个集合内的元素信息作为状态且状态总数为指数级别的dp叫做
状压dp。特点:状态的某一维度或几个维度非常小。

放板子
制作武器

#include<bits/stdc++.h>
using namespace std;
#define reg register
#define int long long
int ti,n,a[12][12],f[1200];
inline int read()
{
	char c=getchar();int f=1,x=0;
	while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return f*x;
}
void dp()
{
	for(reg int t=1;t<(1<<n);++t)//要推导的状态 
	{
		for(reg int i=1;i<=n;++i)//保留
		{
			if(!(t&(1<<(i-1))))
			{
				for(reg int j=1;j<=n;++j)//消失
				{
					if((t&(1<<(j-1))))
					{
						int k=t-(1<<(j-1));//cout<
						f[t]=max(f[t],f[k]+a[i][j]);
					}
				} 
			}
		} 
	}
}
main()
{
	ti=read();
	for(reg int k=1;k<=ti;++k)
	{
		n=read();memset(a,0,sizeof(a)),memset(f,0,sizeof(f));
		for(reg int i=1;i<=n;++i)
		{
			for(reg int j=1;j<=n;++j)
			{
				a[i][j]=read();//f[(1<<(j-1))]=max(f[(1<<(j-1))],a[i][j]);
			}
		}dp();int ans=0;
		for(reg int i=1;i<(1<<n);++i)
		{
			ans=max(ans,f[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}

st表

模板

#include<bits/stdc++.h>
using namespace std;
int n,m,a[100002],f[100002][18];
inline int read()//读入优化 
{
    char c=getchar();int x=0,f=1;
    while(!isdigit(c)) {if(c == '-')f=-1;c=getchar();}
    do{x=x*10+(c^48);c=getchar();}while(isdigit(c));
    return x*f;
}
void st()
{
	for(int i=1;i<=n;++i)
	{
		f[i][0]=a[i];
	}
	for(int j=1;(1<<j)<=18;++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 rmp(int a,int b)
{
	int i=a,j=b;
	int k=(int)(log(j-i+1)/log(2.0));
	int ans=max(f[i][k],f[j+1-(1<<k)][k]);return ans;
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
	}
	st();
	for(int i=1;i<=m;++i)
	{
		int l,r;l=read(),m=read();
		printf("%d\n",rmp(l,r));
	}
}

二维st
理想的正方形

无向tarjan

先放个有向tarjan
上白泽慧音

点双联通,边双联通

割点
void tarjan(int x)
{
	dfn[x]=low[x]=++vistimes;
	int flag=0;
	for(int i=h[x];i;i=edg[i].next)
	{
		int y=edg[i].y;
		if(!dfn[y]){
			tarjan(y);low[x]=min(low[x],low[y]);
			if(low[y]>=dfn[x])
			{
				flag++;if(x!=root||flag>1) if(!cut[x]) cut[x]=1,++ans;
			}
		}else low[x]=min(low[x],dfn[y]);
	}
}
void tarjan(int x,int e_edg)
{
	dfn[x]=low[x]=++vistime;
	for(int i=h[x];i;i=edg[i].next)
	{
		int y=edg[i].y;
		if(!dfn[y])
		{
			tarjan(y,i);low[x]=min(low[y],low[x]);
			if(low[y]>dfn[x])
			{
				bridge[i]=bridge[i^1]=1;
			}
		}
		else if(i!=(e_edg^1))
		{
			low[x]=min(low[x],dfn[y]);
		}
	}
}

冗余路径Redundant Paths
这个题目的思想很有意思呢

BLO-Blockade

二分图

模板见lyd
染色

匈牙利算法

#include<bits/stdc++.h>
using namespace std;
int n,m,e,h[2001],ans,cnt,vis[2001],match[2001];
struct edg{
	int y,next;
}edg[1000001*2];
inline void add(int x,int y)
{
	edg[++cnt].y=y,edg[cnt].next=h[x],h[x]=cnt;
}
bool dfs(int x)
{
	for(int i=h[x];i;i=edg[i].next)
	{
		int y;
		if(!vis[y=edg[i].y])
		{
			vis[y]=1;
			if(!match[y]||dfs(match[y]))
			{
				match[y]=x;return 1;
			}
		}
	}
	return 0;
}
int main()
{
	scanf("%d%d%d",&n,&m,&e);
	for(int i=1;i<=e;++i)
	{
		int x,y;scanf("%d%d",&x,&y);
		if(y>m||x>n) continue;
		add(x,n+y),add(n+y,x);
	}
	for(int i=1;i<=n;++i)
	{
		memset(vis,0,sizeof(vis));if(dfs(i)) ++ans;
	}
	printf("%d\n",ans);
}

基环树

差分约束

这个算法与最短路联系

如若一个系统由n个变量和m个不等式组成,并且这m个不等式对应的系数矩阵中每一行有且仅有一个1和-1,其它的都为0,这样的系统称为差分约束( difference constraints )系统。

看这个阅读
种树

自学

并查集提高

食物链
银河英雄传说
supermarket思路清奇
信息传递

线段树的某些题型

某题

分块·

下阶段任务

博弈论

分块好好看

网络流

高斯消元(有什么用呢)

树链剖分

二分查找树

你可能感兴趣的:(noip)