再闻已是曲中人

无法言说

奇技淫巧

对拍

#include<iostream>
#include<cstdio>
#include<windows.h>
using namespace std;
int main()
{
    for(;;)
    {
    	system("datamaker.exe > data.in");//启动数据生成器 
        system("my.exe < data.in > my.out");//启动“正解”和”暴力“ 
        system("std.exe < data.in > std.out");
        if(system("fc my.out std.out"))//如果出锅,就停下来 
			return-1;
	}
	return 0;
}

或者

#include<iostream>
#include<cstdio>
#include<windows.h>
using namespace std;
int main()
{
    for(;;)
    {
        system("start datamaker.exe");//启动数据生成器 
        Sleep(500);//等待数据输出(极限数据输出大约0.5s) 
        system("start my.exe");//启动“正解”和”暴力“ 
        system("start std.exe");
        Sleep(100);//这个时间取决于你写的暴力的最坏时间 
        if(system("fc my.out std.out"))//如果出锅,就停下来 
        {
            system("pause");
            break;
        }
    }
	return 0;
}

数据生成

注意审题!根据题目决定是否加入重边或自环

随机树

#include<bits/stdc++.h>
int const maxn=111;
int const N=10;
int const M=25;
int const C=10;
std::stringstream str;
int seed;
int n,a[maxn];
int main(int argc,char *argv[])
{
	seed=time(NULL);
	if(argc)
	{
        str.clear();
        str<<argv[1];
        str>>seed;
    }
    srand(seed);
    n=rand()%N+C+1;
    printf("%d\n",n); 
	for(int i=2;i<=n;i++)
	{
		int	x=rand()%i+1;
		if(x==i)
			x--;
		printf("%d %d\n",i,x);
	}
	return 0;
}

长毛树

#include<bits/stdc++.h>
int const maxn=111;
int const N=10;
int const M=25;
int const C=10;
std::stringstream str;
int seed;
int n,a[maxn];
int main(int argc,char *argv[])
{
	seed=time(NULL);
	if(argc)
	{
        str.clear();
        str<<argv[1];
        str>>seed;
    }
    srand(seed);
    n=rand()%N+C+1;
	for(int i=1;i<=n;i++)
		a[i]=i;
	std::random_shuffle(a+1,a+1+n);
	for(int i=2;i<=C;i++)
		printf("%d %d\n",a[i],a[i-1]);
	for(int i=C+1;i<=n;i++)
	{
		int x=rand()%a[i]+1;
		printf("%d %d\n",i,x);
	}
	return 0;
}

菊花图

#include<bits/stdc++.h>
int const maxn=111;
int const N=10;
int const M=25;
int const C=10;
std::stringstream str;
int seed;
int n,a[maxn];
int main(int argc,char *argv[])
{
	seed=time(NULL);
	if(argc)
	{
        str.clear();
        str<<argv[1];
        str>>seed;
    }
    srand(seed);
    n=rand()%N+C;
    printf("%d\n",n); 
 	for(int i=1;i<C;i++)
 		a[i]=i+1;
 	std::random_shuffle(a+1,a+1+C);
 	for(int i=1;i<C;i++)
 		printf("%d %d\n",a[i],1);
	 for(int i=C+1;i<=n;i++)
 	{
 		int x=rand()%C+1;
 		printf("%d %d\n",i,x);
 	}
 	return 0;
}

双星树

#include<bits/stdc++.h>
int const maxn=111;
int const N=10;
int const M=25;
int const C=5;
std::stringstream str;
int seed;
int n,a[maxn],vis[maxn];
int cnt1,cnt2;
int main(int argc,char *argv[])
{
	seed=time(NULL);
	if(argc)
	{
        str.clear();
        str<<argv[1];
        str>>seed;
    }
    srand(seed);
    n=rand()%N+2*C;
    printf("%d\n",n); 
    int root1=rand()%n+1;
    int root2=rand()%n+1;
    if(root1==root2)
    	root2=(root2+1)%n+1; 
    printf("%d %d\n",root1,root2);
    vis[root1]=vis[root2]=true;
 	for(int i=1;i<=n;i++)
 		a[i]=i;
 	std::random_shuffle(a+1,a+1+n);
 	for(int i=1;i<=n;i++)
 	{
 		if(cnt1==C)
 			break;
 		if(vis[a[i]])
 			continue;
	 	cnt1++;printf("%d %d\n",root1,a[i]);
		vis[a[i]]=true;
 	}
 	for(int i=1;i<=n;i++)
 	{
 		if(cnt2==C)
 			break;
 		if(vis[a[i]])
 			continue;
		cnt2++,printf("%d %d\n",root2,a[i]);
		vis[a[i]]=true;
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[a[i]])
 			continue;
 		int x=rand()%a[i]+1;
 		printf("%d %d\n",a[i],x);
 	}
	 return 0;
}

蜘蛛树

#include<bits/stdc++.h>
int const maxn=111;
int const N=10;
int const M=25;
int const C=5;
std::stringstream str;
int seed;
int n,a[maxn],vis[maxn];
int cnt1,cnt2;
int main(int argc,char *argv[])
{
	seed=time(NULL);
	if(argc)
	{
        str.clear();
        str<<argv[1];
        str>>seed;
    }
    srand(seed);
    n=rand()%N+3*C;
    printf("%d\n",n);
    for(int i=1;i<=C;i++)
    	printf("%d %d\n",i,i+1),vis[i]=true,vis[i+1]=true;
    int root1=rand()%n+1;
    while(vis[root1])
    	root1=rand()%n+1;
    vis[root1]=true;
    int root2=rand()%n+1;
    while(vis[root2])
    	root2=rand()%n+1;
    vis[root2]=true;
    printf("%d %d\n%d %d\n",root1,1,root2,C+1);
    vis[root1]=vis[root2]=true;
 	for(int i=1;i<=n;i++)
 		a[i]=i;
 	std::random_shuffle(a+1,a+1+n);
 	for(int i=1;i<=n;i++)
 	{
 		if(cnt1==C)
 			break;
 		if(vis[a[i]])
 			continue;
	 	cnt1++;printf("%d %d\n",root1,a[i]);
		vis[a[i]]=true;
 	}
 	for(int i=1;i<=n;i++)
 	{
 		if(cnt2==C)
 			break;
 		if(vis[a[i]])
 			continue;
		cnt2++,printf("%d %d\n",root2,a[i]);
		vis[a[i]]=true;
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[a[i]])
 			continue;
 		int x=rand()%a[i]+1;
 		printf("%d %d\n",a[i],x);
 	}
	 return 0;
}

伪去重离散化

//unique(u,v)返回去重(伪)后的地址+1,被删掉的会加到后面,以返回值为首地址
int const maxn=1e5+10;
int a[maxn], t[maxn];
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
    scanf("%d",a[i]),t[i]=a[i];
sort(t+1,t+n+1);//排序,找相对大小
m=unique(t+1,t+1+n)-t-1;//m为不重复的元素的个数
for(int i=1; i<=n; i++)
    a[i]=lower_bound(t+1,t+1+m,a[i])-t;//返回b中第一个大于等于a[i]的位置

手写abs

  • 整数范围 int abs(int x){ return (x^(x>>31))-(x>>31);}
  • 实数范围double abs(double x){ return x>esp:x?-x;}

手写swap

void swap(int &a,int &b){a^=b;b^=a;a^=b;}

判断整数奇偶

int pan(int x){return x&1;}

判断两数是否同号

int pan(int x,int y){return !((x^y)>>31);}

构造函数重载运算符

struct node
{
    int nd,dis;
    bool operator<(const node &b)const
    {
        return dis>b.dis;
    }
    //从大到小排序
    node(int nd=0,int dis=0):
        nd(nd),dis(dis){}
};

图论

欧拉图

欧拉回路

欧拉通路

Topsort

解决具有严格更新顺序的图论问题
一般结合DP使用(如树形DP)

void topsort(int s)
{
	std::queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=e[i].next)
		{
			int v=e[i].to;
			ind[v]--;
			f[v]=f[u]+change;
			if(!ind[v])
				q.push(v);
		}
	}
}

求最长拓扑链

把queue改成结构体,跑topsort的时候顺便维护一下深度

生成树

最小生成树

堆优化Prim

prim每次找到集合内到集合外的最短边
这样我们就可以把每个点扩展到的边压成点放进小根堆里堆里
如果u已经在集合内,直接continue
每次取堆顶即可

struct node
{
	int id,dis;
	bool operator <(const node &b)const
	{
		return dis>b.dis;
		//优先队列默认大根堆
		//这里把他重载成小根堆
	}
	node(int id=0,int dis=0):
		id(id).dis(dis){}
};
void prim(int s)
{
	std::priority_queue<node>q;
	q.push(node(s,0));
	cost[s]=0;
	while(!q.empty())
	{
		int u=q.top().id,dis=q.top().dis;
		q.pop();
		if(set[u])
			continue;
		ans+=dis;
		set[u]=true;
		//注意,dijkstra可以不加访问标记,因为dijkstra能保证每个点只入队一次
		//prim则不能保证
		for(int i=head[u];i;i=e[i].next)
		{
			int v=e[i].to,w=e[i].w;
			if(cost[v]>w)
				cost[v]=w,q.push(node(v,w));
		}
	}
	printf("%d",ans);
}
Kruskal
int find(int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
int cmp(RE x,RE y)
{
	return x.w<y,w;
}
void kruskal()
{
	std::sort(e+1,e+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        if(num==n-1)
            break;
        int u=e[i].u,v=e[i].v,w=e[i].w;
        int fu=find(u),fv=find(v);
        if(fu==fv)
            continue;
        num++;ans+=w;
        fa[fv]=fu;
    }
	pintf("%d",ans);
}

瓶颈生成树

无向图 G G G的一颗瓶颈生成树是这样的一颗生成树,
它最大的边权值在 G G G的所有生成树中是最小的。瓶颈生成树的值为T中最大权值边的权。
最小生成树一定是瓶颈生成树
瓶颈生成树不一定是最小生成树

Kruskal求瓶颈生成树

由于kruskal的性质,第一棵求出的生成树就是瓶颈生成树

最小瓶颈路

最小瓶颈路问题是指在一张无向图中,有多组询问,每组询问提供一个一个点对 ( u , v ) (u,v) (u,v),需要找出从 u u u v v v的一条简单路径,使路径上所有边中最大值最小

解法

最小生成树中u到v的路径一定是最小瓶颈路之一
因此我们可以先搞一棵 M S T MST MST,然后问题转化为了求 M S T MST MST ( u , v ) (u,v) (u,v)的最大边权
这个用倍增LCA或者树剖都能搞

非严格次小生成树

求出一棵生成树,其所有边的权值之和仅次于最小生成树的权值之和

解法

先求出 M S T MST MST,然后枚举加入不在 M S T MST MST上的边,这样势必会构成环,我们则需要在环上断开一条边回到树形结构
自此问题转化为求 M S T MST MST ( u , v ) (u,v) (u,v)两个点之间的最大边权,这显然是个最小瓶颈路问题,解法同上

最小增量生成树

从包含n个点的空图开始,依次加入m条带权边。每加入一条边输出当前图的最小生成树的权值,不连通输出无解

解法
int main()
{
	scanf("%d%d"&n,&m);
	for(int u,v,w,i=1;i<=m;i++)
	{
		scanf("%d%d%d“,&u,&v,&w1);
		if(num<n-1)
			printf("-1\n");
		int fu=find(u),fv=find(v);
		if(fu==fv)
		{
			int w2=(u,v)的最大边权(最小瓶颈路解法)
			MST-=Map[u][v];
			Map[u][v]=std::min(Map[u][v],w2);
			MST+=Map[u][v];
		}
		else
		{
			fa[fv]=fu;
			MST+=w1;
			Map[u][v]=w1;
		}
		if(n>=n-1)
			printf("%d\n",MST);
		num++;
	}	
}

最短路

Floyd

Dijkstra

SPFA

差分约束

Tarjan

缩点

求联通分量

数据结构

线段树

#include<bits/stdc++.h>
int const maxn=111;
struct Tree
{
	int lc,rc,sum,tag;
}a[maxn<<1];
int t=1;
int w[maxn];
void pushup(int u)
{
	a[u].sum=a[a[u].lc].sum+a[a[u].rc].sum;
}
void build(int u,int l,int r)
{
	if(l==r)
	{
		a[u].sum=w[l];return;
	}
	int mid=l+r>>1;
	a[u].lc=++t;
	build(a[u].lc,l,mid);
	a[u].rc=++t;
	build(a[u].rc,mid+1,r);
	pushup(u);
}
void pushdown(int u,int l,int r)
{
	int lc=a[u].lc,rc=a[u].rc,tag=a[u].tag;
	int mid=l+r>>1;
	a[lc].sum+=tag*(mid-l+1);
	a[lc].tag+=tag;
	a[rc].sum+=tag*(mid-r);
	a[rc].tag+=tag;
	a[u].tag=0;
}
void update_plus(int u,int l,int r,int ll,int rr,int dlt)
{
	if(l==ll&&r==rr)
	{
		a[u].sum+=dlt*(l-r+1);
		a[u].tag+=dlt;
		return;
	}
	pushdown(u,l,r);
	int mid=l+r>>1;
	if(rr<=mid)
		update_plus(a[u].lc,l,mid,ll,rr,dlt);
	else if(ll>mid)
		update_plus(a[u].rc,mid+1,r,ll,rr,dlt);
	else
		update_plus(a[u].lc,l,mid,ll,mid,dlt),update_plus(a[u].rc,mid+1,r,mid+1, rr,dlt);
	pushup(u);
}
int query(int u,int l,int r,int ll,int rr)
{
	if(l==ll&&r==rr)
		return a[u].sum;
	pushdown(u,l,r);
	int mid=l+r>>1;
	return rr<=mid?query(a[u].lc,l,mid,ll,rr):(ll>mid?query(a[u].rc,mid+1,r,ll,rr):query(a[u].lc,l,mid,ll,mid)+query(a[u].rc,mid+1,r,mid+1,rr));
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&w[i]);
	build(1,1,n); 
	for(int op,x,y,z,i=1;i<=m;i++)
	{
		scanf("%d%d%d",&op,&x,&y);
		if(op==1)
		{
			scanf("%d",&z);
			update_plus(1,1,n,x,y,z);
		}
		else
			printf("%d\n",query(1,1,n,x,y));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值