模板整理

模板整理

目录

杂货

  1. 快读,快写,关闭流同步
  2. KMP,拓展KMP
  3. 对拍
  4. 离散化
  5. 扩展欧几里得求逆元
  6. 线性求逆元

图论

  1. 最短路,dijkstra堆优化,spfa,floyd最短环。
  2. 拓展并查集,最小生成树
  3. 二分图染色,二分图最大匹配
  4. tarjan缩点
  5. 树上倍增LCA,树链剖分LCA
  6. 树的直径,树的重心
  7. 树的dfs序

数据结构&STL

  1. 链表
  2. 单调栈
  3. 单调队列
  4. ST表
  5. hash-->map
  6. Set
  7. 二维树状数组,线段树功能树状数组
  8. 线段树
  9. 求最大子段和的线段树
  10. 字典树
  11. 分块,莫队

动态规划

  1. 树形DP
  2. 数位DP
  3. 状态压缩
  4. 单调队列DP

快读,快写,关闭流同步

#include<bits/stdc++.h>
using namespace std;
#define rt register int
inline int read(){
    int f=1,x=0;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+int(c-'0');c=getchar();}
    return f*x;
}
inline void write(int x)
{
    if(x<0)
        putchar('-'),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    int n;
    n=read();
    write(n);
}

KMP

#include<bits/stdc++.h>
using namespace std;
#define rt register int 
int ne[1000005];
char s1[1000005],s2[1000005];
int main()
{
	ios::sync_with_stdio(false);
	cin>>s1+1>>s2+1;
	int la=strlen(s1+1),lb=strlen(s2+1);
	for(rt i=2,j=0;i<=lb;++i)//从2开始是因为1只有一个字母
	{
		while(j&&s2[j+1]!=s2[i])j=ne[j];//如果匹配失败就要寻找较小的最大公共前后缀继续匹配,而它的位置正是ne[j]
		if(s2[j+1]==s2[i])++j;//这两行本质是DP的两种情况
		ne[i]=j;
	}
	for(rt i=1,j=0;i<=la;++i){
		while(j&&s1[i]!=s2[j+1])j=ne[j];
		if(s2[j+1]==s1[i])++j;
		if(j==lb){
			cout<<i-lb+1<<endl;j=ne[j];//这里不要忘了回去
		}
	}
	for(rt i=1;i<=lb;++i)
		cout<<ne[i]<<" ";
	return 0;
}

拓展KMP

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e7 + 5;
char p[MAXN], s[MAXN];
int pl, sl, z[MAXN], ext[MAXN];
void getZ() {
    z[0] = pl;
    int now = 0;
    while (now + 1 < pl && p[now] == p[now + 1]) now++;
    z[1] = now;
    int p0 = 1;
    for (int i = 2; i < pl; ++i) {
        if (i + z[i - p0] < p0 + z[p0]) {
            z[i] = z[i - p0];
        } else {
            now = p0 + z[p0] - i;
            now = max(now, 0);
            while (now + i < pl && p[now] == p[now + i]) now++;
            z[i] = now;
            p0 = i;
        }
    }
}
void exkmp() {
    getZ();
    int now = 0;
    while (now < pl && now < sl && p[now] == s[now]) now++;
    ext[0] = now;
    int p0 = 0;
    for (int i = 1; i < sl; ++i) {
        if (i + z[i - p0] < p0 + ext[p0]) {
            ext[i] = z[i - p0];
        } else {
            now = p0 + ext[p0] - i;
            now = max(now, 0);
            while (now < pl && now + i < sl && p[now] == s[now + i]) now++;
            ext[i] = now;
            p0 = i;
        }
    }
}
int main() {
    scanf("%s%s", s, p);
    pl = strlen(p);
    sl = strlen(s);
    exkmp();
    long long a0 = 0, a1 = 0;
    for (int i = 0; i < pl; ++i) {
        a0 ^= 1LL * (i + 1) * (z[i] + 1);
    }
    for (int i = 0; i < sl; ++i) {
        a1 ^= 1LL * (i + 1) * (ext[i] + 1);
    }
    printf("%lld\n%lld\n", a0, a1);
    return 0;
}

对拍

#include <cstdio>
#include <cstdlib>
#include <ctime>
ll random(ll mod)
{
    ll n1, n2, n3, n4, ans;
    n1 = rand();
    n2 = rand();
    n3 = rand();
    n4 = rand();
    ans = n1 * n2 % mod;
    ans = ans * n3 % mod;
    ans = ans * n4 % mod;
    return ans;
}
int main()
{
    srand((unsigned)time(0));
    ll n;
    while (1)
    {
        n = random(1000000);
        cout << n << endl;
    }
    return 0;
}


#include <cstdio>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
    while (1) //一直循环,直到找到不一样的数据
    {
        system("data.exe > in.txt");
        system("baoli.exe < in.txt > baoli.txt");
        system("std.exe < in.txt > std.txt");
        if (system("fc std.txt baoli.txt")) //当 fc 返回1时,说明这时数据不一样
            break;                          //不一样就跳出循环
    }
    return 0;
}



离散化

#include<bits/stdc++.h>
#define MAXN 1000010
using namespace std;
int a[MAXN], b[MAXN];
int x, y;
int n;
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i){
		scanf("%d", &a[i]); b[i] = a[i];
	}
	sort(a + 1, a + n + 1);
	x = unique(a + 1, a + n + 1) - a - 1;
	for(int i = 1; i <= n; ++i){
		b[i] = lower_bound(a + 1, a + x + 1, b[i]) - a; 
	}
	for(int i = 1; i <= n; ++i)
		printf("%d ", b[i]);
	puts("");
	
	return 0;
}

扩展欧几里得求逆元

#include<bits/stdc++.h>
using namespace std;
int exgcd(int a, int b, int &x, int &y){//返回gcd(a,b) 并求出解(引用带回),每次函数中值的改动要传回上一层函数
	if(b==0){
	    //a*1+b*0=gcd(a,b)是gcd辗转相除法最后的到的特解。
		x = 1, y = 0;
		return a;//此时的a就是gcd(a,b)
	}
	int x1,y1,gcd;
	gcd = exgcd(b, a%b, x1, y1);
	x = y1, y = x1 - a/b*y1;
	return gcd; 
}
int main(){
	int n,a,b,x,y;
	cin>>a>>b;
	exgcd(a,b,x,y);
	cout<<(x%b+b)%b<<endl;
	return 0;
}
/*
/*
	因为
		gcd(a,b)=gcd(b,a%b)
	而
		bx′+(a%b)y′=gcd(b,a%b)
		
	等于
		bx′+(a−⌊a/b⌋∗b)y′=gcd(b,a%b)
	转化
		ay′+b(x′−⌊a/b⌋∗y′)=gcd(b,a%b)=gcd(a,b)
	
	故而
		x=y′,y=x′−⌊a/b⌋∗y′
*/

线性求逆元

首先我们有一个,1*1≡1(mod p)

然后设 p=k∗i+r,(1<r<i<p) 也就是 k 是 p/i的商,r 是余数 。

再将这个式子放到(mod p)意义下就会得到:

k∗i+r≡0(mod p)

-k∗i≡r(mod p)

两边同时除以(r*i)

-k*inv[r]≡inv[i](mod p)

换元

inv[i]≡-p/i*inv[p%i](mod p)

inv[i]≡(p-p/i)*inv[p%i](mod p)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3000010;
int inv[N],n,p;
int main()
{
	scanf("%d%d",&n,&p);
	inv[1] = 1;
	puts("1");
	for(int i = 2;i <= n;i ++)
	{
		inv[i] = (ll)(p - p / i) * inv[p % i] % p;
		printf("%d\n",inv[i]);
	}
	return 0;
}

最短路

dijkstra

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e6 + 10;
int n,m;
int h[N],w[N],e[N],ne[N],idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx ++;
}
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1] = 0;
    priority_queue<PII,vector<PII>,greater<PII>> heap;
    heap.push({0,1});
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int v = t.second;
        if(st[v])continue;
        st[v] = true;
        for(int i = h[v];i != -1;i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[v] + w[i])
            {
                dist[j] = dist[v] + w[i];
                heap.push({dist[j],j});
            }
        }
    }
    if(dist[n] == 0x3f3f3f3f)return -1;
    return dist[n];
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    memset(h,-1,sizeof h);
    while(m --)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
    }
    cout <<dijkstra();
    return 0;
}

spfa

spfa判断最短路
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,h[N],w[N],e[N],ne[N],idx,dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    queue<int> q;
    q.push(1);
    st[1]=true;
    while(!q.empty()){
        int t=q.front();
        q.pop();
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                if(!st[j]){
                    q.push(j);
                    st[j]=true;
                }
            }
        }   
    }
    return dist[n];
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    int t=spfa();
    if(t==0x3f3f3f3f)printf("impossible");
    else printf("%d\n",t);
    return 0;
}

最短环

1.注意第一个循环,i,j,k互不相等

2.这里解释一下为什么第一个循环一定要在第二个循环前面。第二个循环只改变了dis[i] [j]

如果第二个循环先,那么这个dis[i] [j] 有可能是包含了k的最短路,这时候就不能用了(路径重复)

#include<bits/stdc++.h>
using namespace std;
int edge[105][105];
int dis[105][105];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i!=j) edge[i][j]=dis[i][j]=1e8;//初始化
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
		scanf("%d%d%d",&u,&v,&w); 
        edge[u][v]=edge[v][u]=w;
        dis[u][v]=dis[v][u]=edge[u][v];
    }
    int ans=1e8;
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<k;i++)
            for(int j=i+1;j<k;j++)// 注意这里i,j不能相等
                ans=min(ans,dis[i][j]+edge[j][k]+edge[k][i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    }
    if(ans==1e8) printf("No solution.\n");
    else printf("%d\n",ans);
    return 0;
}
/*
4 5
1 2  
1 3 
1 4 
2 4
3 4 
*/

最短环变式

问你有几个最短环

#include<bits/stdc++.h>
using namespace std;
int edge[305][305];
int dis[305][305];
int res;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i!=j) edge[i][j]=dis[i][j]=1e8;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
		scanf("%d%d",&u,&v); 
        edge[u][v]=edge[v][u]=1;
        dis[u][v]=dis[v][u]=edge[u][v];
    }
    int ans=5e8;
    for(int k=1;k<=n;k++){
        for(int i=1;i<k;i++)
            for(int j=i+1;j<k;j++)
			{
//				printf("i:%d j:%d k:%d\n",i,j,k);
//				printf("ans:%d\n",ans);
//				printf("now:%d\n",dis[i][j]+edge[j][k]+edge[k][i]);
            	if(ans>(dis[i][j]+edge[j][k]+edge[k][i]))
            	{	
//            		printf("change\n");
            		ans=dis[i][j]+edge[j][k]+edge[k][i];
            		res=1;
				}else if(ans==dis[i][j]+edge[j][k]+edge[k][i]) ++res;
			}
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    }
    if(ans==1e8) printf("0\n");
    else printf("%d\n",res);
    return 0;
}
/*
4 5
1 2  
1 3 
1 4 
2 4
3 4 
*/
spfa判断有负环
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,idx,h[N],w[N],e[N],ne[N];
int dist[N],cnt[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool spfa()
{
    queue<int> q;
    for(int i=1;i<=n;i++)
    {
        st[i]=true;
        q.push(i);
    }
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n)return true;
                if(!st[j])
                {
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    if(spfa())printf("Yes");
    else  printf("No");
    return 0;
}

并查集,最小生成树

种类并查集

/*
来自《食物链》
*/
#include<iostream>
using namespace std;
const int maxn=1e6;
int fa[maxn],n,k,ans;
int find(int x)
{
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}
int join(int x,int y)
{
	int f1=find(fa[x]),f2=find(fa[y]);
	fa[f1]=f2;
}
int main()
{
	
	scanf("%d%d",&n,&k);
	for(int i=1;i<=(n<<1)+n;i++)
	fa[i]=i;
	int c1,c2,c3;
	for(int i=1;i<=k;i++)
	{
		scanf("%d%d%d",&c1,&c2,&c3);
		if(c2>n||c3>n)
		{
			ans++;
			continue;
		}
		if(c1==1)
		{
			if(find(c2+n)==find(c3)||find(c2+(n<<1))==find(c3))
			{
				ans++;
				continue;
			}
			join(c2,c3);
			join(c2+n,c3+n);
			join(c2+(n<<1),c3+(n<<1));
		}
		else if(c1==2)
		{
			if(c2==c3){
				ans++;
				continue;
			}
			if(find(c2)==find(c3)||find(c2+(n<<1))==find(c3))
			{
				ans++;
				continue;
			}
			join(c2,c3+(n<<1));
			join(c2+n,c3);
			join(c2+(n<<1),c3+n);
		}
	}
	printf("%d",ans);
	return 0;
}

最小生成树

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=200010;
int n,m;
int p[N];
struct edge{
    int u,v,w;
    bool operator< (const edge &W)const{
        return w<W.w;
    }
}edges[N];
int find(int x){if(p[x]!=x)p[x]=find(p[x]);return p[x];}
int kruskal()
{
    sort(edges,edges+m);
    for(int i=1;i<=n;i++)p[i]=i;
    int res=0,cnt=0;
    for(int i=0;i<m;i++)
    {
        int a=edges[i].u,b=edges[i].v,w=edges[i].w;
        a=find(a),b=find(b);
        if(a!=b){
            p[a]=b;
            res+=w;
            cnt++;
        }
    }
    if(cnt<n-1)return inf;
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        edges[i]={u,v,w};
    }
    int t=kruskal();
    if(t==inf)printf("impossible\n");
    else printf("%d\n",t);
    return 0;
}

二分图染色,二分图最大匹配

二分图染色

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int e[N],ne[N],h[N],idx;
int st[N];
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool dfs(int u,int color){
    st[u]=color;
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]){
            if(!dfs(j,3-color))return false;
        }else if(st[j]==color)return false;
    }
    return true;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    while(m--){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }
    bool flag=true;
    for(int i=1;i<=n;i++){
        if(!st[i]){
            if(!dfs(i,1)){
                flag=false;
                break;
            }
        }
    }
    if(flag)printf("Yes\n");
    else printf("No\n");
    return 0;
}

二分图最大匹配

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n1,n2,m;
int h[N],ne[N],e[N],idx;
bool st[N];
int match[N];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int find(int x)
{
    for(int i=h[x];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!st[j]){
            st[j]=true;
            if(!match[j]||find(match[j]))
            {
                match[j]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    memset(h,-1,sizeof h);
    scanf("%d%d%d",&n1,&n2,&m);
    while(m--){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    int res=0;
    for(int i=1;i<=n1;i++)
    {
        memset(st,false,sizeof st);
        if(find(i))
        res++;
    }
    printf("%d",res);
    return 0;
}

tarjan缩点

/*受欢迎的牛*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5,M = 5e4 + 5;
int n,m,x,y,hd[N],idx,timestamp,top,cnt;
int dfn[N],sum,res,siz[N],low[N],stk[N],out[N],id[N];
bool in_stk[N];
struct Edge{
	int to,nxt;
}edge[M];
void add(int x,int y)
{
	edge[++ idx].to = y;edge[idx].nxt = hd[x];hd[x] = idx;
}

void tarjan(int u){
	dfn[u] = low[u] = ++ timestamp;
    stk[ ++ top] = u, in_stk[u] = true;
    for (int i = hd[u];i; i = edge[i].nxt)
    {
        int j = edge[i].to;
        if (!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_stk[j]) low[u] = min(low[u], low[j]);
    }

    if (dfn[u] == low[u])
    {
        ++ cnt;
        int y;
        do {
            y = stk[top -- ];
            in_stk[y] = false;
            id[y] = cnt;
            siz[cnt] ++ ;
        } while (y != u);
    }
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= m;++ i)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for(int i = 1;i <= n;++ i)
		if(!dfn[i])tarjan(i);
	for (int i = 1; i <= n; i ++ )
        for (int j = hd[i]; j; j = edge[j].nxt)
        {
            int k = edge[j].to;
            int a = id[i], b = id[k];
            if (a != b) out[a] ++ ;
        }
    for(int i = 1;i <= cnt;i ++)
    {
    	if(!out[i])
		{
			++ sum;	
			res += siz[i];
		}
    	if(sum >= 2){
    		res = 0;
    		break;
		}
	}
	printf("%d",res);
	return 0;
}

LCA

树上倍增求LCA

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+5;

int n,m,s,idx,h[N],dep[N],f[N][30];

struct edge{
	int to,ne;
}edges[N];

void add(int u,int v)
{	
	idx ++;
	edges[idx].to = v,edges[idx].ne = h[u],h[u] = idx;
	edges[++ idx].to = u,edges[idx].ne = h[v],h[v] = idx;
}

void dfs(int u,int fa)
{
	dep[u] = dep[fa] + 1;
	for(int i = 1;(1 << i) <= dep[u];i ++)
		f[u][i] = f[f[u][i - 1]][i - 1];
	
	for(int i = h[u];i;i = edges[i].ne)
	{
		int v = edges[i].to;
		if(v == fa)continue;
		f[v][0] = u;
		dfs(v,u);
	}
}

int LCA(int a,int b)
{
	if(dep[a] < dep[b])swap(a,b);
	for(int i = 20;i >= 0;i --)
	{
		if(dep[f[a][i]] >= dep[b]) a = f[a][i];
		if(a == b)return a;
	}
	
	for(int i = 20;i >= 0;i --)
	{
		if(f[a][i] != f[b][i])
		{
			a = f[a][i];
			b = f[b][i];
		}
			
	}
	return f[a][0];	
}
int main()
{
	cin >> n >> m >> s;
	for(int i = 1;i < n;i ++)
	{
		int a,b;
		cin >> a >> b;
		add(a,b);
	}
	dfs(s,0);
	for(int i = 1;i <= m;i ++)
	{
		int a,b;
		cin >> a >> b;
		cout << LCA(a,b) << endl;
	}
	return 0;
}

树链剖分

#include<bits/stdc++.h>

using namespace std;

const int N = 500010;

struct edge{
	int to,ne;
}edges[1000005];

int n,m,root;
int son[N],size[N],top[N],dep[N],f[N],idx,h[N];

void add(int u,int v)
{
	++ idx;
	edges[idx].to = v;
	edges[idx].ne = h[u];
	h[u] = idx;
	++ idx;
	edges[idx].to = u;
	edges[idx].ne = h[v];
	h[v] = idx;
}

void dfs1(int u){
	size[u] = 1;
	dep[u] = dep[f[u]] + 1;
	for(int i = h[u];i;i = edges[i].ne)
	{
		int v = edges[i].to;
		if(v == f[u])continue;
		f[v] = u;
		dfs1(v);
		size[u] += size[v];
		if(size[son[u]] < size[v])
			son[u] = v;
	}
}

void dfs2(int u,int tp)//tp是链的顶点 
{
	top[u] = tp;
	if(son[u])
		dfs2(son[u],tp);
	
	for(int i = h[u];i;i = edges[i].ne)
	{
		int v = edges[i].to;
		if(v != f[u] && v != son[u])
			dfs2(v,v);
	}
}

int lca(int u,int v)
{
	while(top[u] != top[v])
	{
		
		if(dep[top[u]] < dep[top[v]])
			swap(u,v);
		
		u = f[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}

int main()
{	
	int x,y;
	
	cin >> n >> m >> root;
	for(int i = 1;i < n;i ++)
	{
		cin >> x >> y;
		add(x,y);
	}
	dfs1(root);
	dfs2(root,root);
	for(int i = 1;i <= m;i ++)
	{
		cin >> x >> y;
		cout << lca(x,y) << endl;
	}
}

树的直径

/*逃学的小孩*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=200010;
struct edge{
    int to,next,w;
}e[maxn<<1];
int n,m,cnt;
int dis1[maxn],dis2[maxn],last[maxn];
int st,ed;
inline void add(int u,int v,int w){
    cnt++;
    e[cnt].to=v;
    e[cnt].next=last[u];
    last[u]=cnt;
    e[cnt].w=w;
}
void dfs1(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis1[v]=dis1[u]+e[i].w;
        if(dis1[v]>dis1[st]) st=v;
        dfs1(v,u);
    }
}
void dfs2(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis2[v]=dis2[u]+e[i].w;
        if(dis2[v]>dis2[ed]) ed=v;
        dfs2(v,u);
    }
}
void dfs3(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis1[v]=dis1[u]+e[i].w;
        dfs3(v,u);
    }
}
void dfs4(int u,int fa){
    for(int i=last[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dis2[v]=dis2[u]+e[i].w;
        dfs4(v,u);
    }
}
signed main(){
    cin>>n>>m;
    for(int i=1,u,v,w;i<=m;i++){
        scanf("%lld %lld %lld",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
    dfs1(1,0);
    dfs2(st,0);
    int ans=dis2[ed];
    memset(dis1,0,sizeof(dis1)),memset(dis2,0,sizeof(dis2));
    dfs3(st,0),dfs4(ed,0);
    int tmp=0;
    for(int i=1;i<=n;i++){
        int d=min(dis1[i],dis2[i]);
        if(d>tmp) tmp=d;
    }
    ans+=tmp;
    cout<<ans;
}

树的重心

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;

int n;
int idx,e[N],h[N],ne[N];
int ans =N,s[N],stl[N];

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
}

int dfs(int u)
{   
    int size = 1,res = 0;
    stl[u] = 1;
    for(int i = h[u];i != -1;i = ne[i])
    {   
        int j = e[i];
        if(!stl[j])
        {
            int s = dfs(j);
            res = max(res,s);
            size += s;
        }
    }
    
    res = max(res,n - size);
    ans = min(ans,res);
    
    return size;
}

int main()
{   
    memset(h,-1,sizeof h);
    cin >> n;
    int a,b;
    for(int i = 0;i < n-1;i ++)
    {
        cin >> a >> b;
        add(a,b);
        add(b,a);
    }
    dfs(1);
    cout << ans;
    return 0;
}

树的dfs序

#include<cstdio>
using namespace std;
const int maxn=1e5+10;
int n;
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int id[maxn],cnt;
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs(int x,int f)
{
    id[++cnt]=x;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==f)
            continue;
        dfs(y,x);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=cnt;i++)
        printf("%d ",id[i]);
    return 0;
}

链表

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int m,e[N],l[N],r[N],idx;
void init()
{
    l[1] = 0,r[0] = 1;
    idx = 2;
}

void add(int k,int x)
{
    e[idx] = x;
    l[idx] = k;
    l[r[k]] = idx;
    r[idx] = r[k];
    r[k] = idx;//r[k]一定要最后才换
    idx ++;
}

void remove(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}

int main()
{
    cin >> m;
    init();
    while(m --)
    {
        string op;
        cin >> op;
        int k,x;
        if(op == "R")
        {
            cin >> x;
            add(l[1],x);
        }else if(op == "L")
        {
            cin >> x;
            add(0,x);
        }else if(op == "D")
        {
            cin >> k;
            remove(k + 1);//k-1+2,idx=2

        }else if(op == "IL")
        {
            cin >> k >> x;
            add(l[k + 1],x);
        }else{
            cin >> k >> x;
            add(k + 1,x);
        }
    }
    for(int i = r[0];i != 1;i = r[i])cout << e[i] << " ";
    return 0;
}

单调栈

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int stk[N],tt;
int main()
{
    int n;
    cin >> n;
    while(n --)
    {
        int x;
        cin >> x;
        while(tt && stk[tt] >= x)tt --;
        if(!tt)cout<<"-1"<<" ";
        else cout<<stk[tt]<<" ";
        stk[ ++ tt] = x ;
    }
    return 0;
}

单调队列

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int a[N],q[N],hh,tt = -1;
int main()
{
    int n,k;
    cin >> n >> k;
    for(int i = 0;i < n;i ++)
    {
        cin >> a[i];
        if(i - k + 1 > q[hh])++ hh;
        while(hh <= tt && a[i] <= a[q[tt]]) -- tt;//使得序列单调递增,第一个就是最小的,q[]是存下标的,查询的时间复杂度为O(1)
        q[++ tt] = i;
        if(i + 1 >= k)cout<<a[q[hh]]<<" ";
    }
    puts("");
    hh = 0,tt = -1;
    for (int i = 0; i < n; ++ i)
    {
        if (i - k + 1 > q[hh]) ++ hh;
        while (hh <= tt && a[i] >= a[q[tt]]) -- tt;
        q[++ tt] = i;
        if (i + 1 >= k) printf("%d ", a[q[hh]]);
    }
    return 0;
}

ST表

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int Max[MAXN][21];
int Query(int l,int r)
{
    int k=log2(r-l+1); 
    return max(Max[l][k],Max[r-(1<<k)+1][k]);//把拆出来的区间分别取最值 
}
int main()
{
    #ifdef WIN32
    freopen("a.in","r",stdin);
    #endif
    int N=read(),M=read();
    for(int i=1;i<=N;i++) Max[i][0]=read();
    for(int j=1;j<=21;j++)
        for(int i=1;i+(1<<j)-1<=N;i++)//注意这里要控制边界 
            Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);//如果看不懂边界的话建议好好看看图 
    for(int i=1;i<=M;i++)
    {
        int l=read(),r=read();
        printf("%d\n",Query(l,r));
    }
    return 0;
}

Set

#include<bits/stdc++.h>
#define rt register int 
using namespace std;
int n,m1,m2,ans1[100005],ans2[100005];
int res;
#define pii pair<int,int>
set<pii > a,b;
set<pii > :: iterator id;
pii tmp;
inline int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return f*x;
}
inline void write(int x){
	if(x<0){
		putchar('-');x=-x;
	}
	if(x>9)
		write(x/10);
	putchar(x%10+'0');
}
inline int worka(){
	int res=0,now=0;
	while(true){
		id=a.lower_bound(make_pair(now,now));
		if(id==a.end())break;
		++res;now=id->second;
		a.erase(id);
	}
	return res;
}
inline int workb(){
	int res=0,now=0;
	while(true){
		id=b.lower_bound(make_pair(now,now));
		if(id==b.end())break;
		++res;now=id->second;
		b.erase(id);
	}
	return res;
}
int main(){
	n=read();m1=read();m2=read();
	for(rt i=1;i<=m1;++i)
	{
		tmp.first=read();tmp.second=read();
		a.insert(tmp);
	}
	for(rt i=1;i<=m2;++i)
	{
		tmp.first=read();tmp.second=read();
		b.insert(tmp);
	}
	ans1[0]=0;ans2[0]=0;
	for(rt i=1;i<=n;++i)
		ans1[i]=ans1[i-1]+worka();
	for(rt i=1;i<=n;++i)
		ans2[i]=ans2[i-1]+workb();
	for(rt i=0;i<=n;++i)
		res=max(res,ans1[i]+ans2[n-i]);
	write(res);
}

二维树状数组

#include <bits/stdc++.h>
const int maxn=4096+5;
typedef long long ll;
ll c[maxn][maxn];
int n,m;
int lowbit(int x){return x & -x;}
void modify(int x,int y,int z){//点(x,y)增加z 
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
            c[i][j]+=z;
}
ll getsum(int x,int y){//求左上角为(1,1)右下角为(x,y) 的矩阵和
    ll tot=0;
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j))
            tot+=c[i][j];
    return tot;
}
void Solve(){
    scanf("%d%d",&n,&m);
    int ch;
    while(~scanf("%d",&ch)){
        if(ch==1){
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            modify(x,y,k);
        }
        else{
            int a,b,c,d;
            scanf("%d%d%d%d",&a,&b,&c,&d);
            printf("%lld\n",getsum(c,d)-getsum(c,b-1)-getsum(a-1,d)+getsum(a-1,b-1));
        }
    }
}
int main(){
    Solve();
    return 0;
}

线段树功能树状数组

#include<bits/stdc++.h>
using namespace std;
#define rt register int 
const int maxn=1e5+5;
long long n,m,a[maxn];
long long asum[maxn];
long long d1[maxn],d2[maxn];
inline int lowbit(int x){return x&(-x);}
inline void add(long long arr[],int i,int x){
	while(i<=n){
		arr[i]+=x;
		i+=lowbit(i);
	}
}
inline void modify(int l,int  r,int x){
	add(d1,l,x);add(d1,r+1,-x);add(d2,l,x*l);add(d2,r+1,-x*(r+1));
}
inline long long getsum(long long arr[],int i){
	long long res=0;
	while(i>0){
		res+=arr[i];
		i-=lowbit(i);
	}
	return res;
}
inline long long F(int i){
	return (asum[i]+(i+1)*getsum(d1,i)-getsum(d2,i));
}
inline long long query(int l,int r)
{
	return F(r)-F(l-1);
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(rt i=1;i<=n;++i)cin>>a[i],asum[i]=asum[i-1]+a[i];
	int op,x,y,k;
	for(rt i=1;i<=m;++i)
	{
		cin>>op;
		if(op==1){
			cin>>x>>y>>k;
			modify(x,y,k);
		}else{
			cin>>x>>y;
			cout<<query(x,y)<<endl;
		}
	}
	return 0;
} 

树状数组求逆序对

#include<bits/stdc++.h>
using namespace std;
#define rt register int 
#define ll long long 
int n,x;
int a[500005],b[500005];
int c[500005];
inline void add(int x){
	while(x<=n){
		c[x]++;
		x+=(x & -x);
	}
}
inline ll Q(int x){
	ll ans=0;
	while(x>0){
		ans+=c[x];
		x-=(x & -x);
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(rt i=1;i<=n;++i){
		cin>>a[i];
		b[i]=a[i]; 
	} 
	sort(a+1,a+n+1);
	x=unique(a+1,a+n+1)-a-1;
	for(rt i=1;i<=n;++i) b[i]=lower_bound(a+1,a+x+1,b[i])-a;
	ll res=0;
	for(rt i=n;i>0;i--){
		add(b[i]);
		res+=Q(b[i]-1);
	}
	cout<<res<<endl;
	return 0;
}

线段树

#include<bits/stdc++.h>
using namespace std;
#define rt register int 
#define ll long long
struct Tree{
	int l,r;
	ll val,add;
}t[400005];
int n,m,o,k,x,y;
int a[100005];
inline void build(int x,int l,int r){
	t[x].l=l;t[x].r=r;
	if(l==r){
		t[x].val=a[l];
		return;
	}
	int mid=(t[x].l+t[x].r)>>1;
	build(2*x,l,mid);
	build(2*x+1,mid+1,r);
	t[x].val=t[2*x].val+t[2*x+1].val;
}
inline void spread(int x){
	if(t[x].add>0){
		t[2*x].val+=1ll*(t[2*x].r-t[2*x].l+1)*t[x].add;
		t[2*x+1].val+=1ll*(t[2*x+1].r-t[2*x+1].l+1)*t[x].add;
		t[2*x].add+=t[x].add;
		t[2*x+1].add+=t[x].add;
		t[x].add=0;
	}
} 
inline void update(int x,int l,int r){
	if(l<=t[x].l and r>=t[x].r){
		t[x].val+=(t[x].r-t[x].l+1)*k;
		t[x].add+=k;
		return;
	}
	spread(x);
	int mid=(t[x].r+t[x].l)>>1;
	if(l<=mid)update(2*x,l,r);
	if(r>mid)update(2*x+1,l,r);
	t[x].val=t[2*x].val+t[2*x+1].val; 
}
inline ll Q(int x,int l,int r){
	if(l<=t[x].l and r>=t[x].r){
		return t[x].val;
	}
	spread(x);
	int mid=(t[x].r+t[x].l)>>1;
	ll ans=0;
	if(l<=mid)ans+=Q(2*x,l,r);
	if(r>mid)ans+=Q(2*x+1,l,r);
	return ans;
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(rt i=1;i<=n;++i)
		cin>>a[i];
	build(1,1,n);
	
	for(rt i=1;i<=m;++i){
		cin>>o;
		if(o==1){
			cin>>x>>y>>k;
			update(1,x,y);
		}else{
			cin>>x>>y;
			cout<<Q(1,x,y)<<endl;
		}
	}
	return 0;
} 

最大子段和线段树

#include<bits/stdc++.h>
const int  N = 2000001;
using namespace std;
int n,m;
struct Tree
{
    int l,r;
    long long maxleft,maxright,sum,ans;
}tree[N];
long long a[N];
void pushup(int k)
{
    tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;
    tree[k].maxleft=max(tree[k*2].maxleft,tree[k*2].sum+tree[k*2+1].maxleft);
    tree[k].maxright=max(tree[k*2+1].maxright,tree[k*2].maxright+tree[k*2+1].sum);
    tree[k].ans=max(max(tree[k*2].ans,tree[k*2+1].ans),tree[k*2].maxright+tree[k*2+1].maxleft);
}

void build(int l,int r,int k)
{
    tree[k].l=l;tree[k].r=r;
    if(l==r)
    {
        tree[k].sum = a[l];
        tree[k].maxleft=tree[k].maxright=tree[k].ans=tree[k].sum;
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,k*2);
    build(mid+1,r,k*2+1);
    pushup(k);
}

Tree ask(int k,int x,int y)
{
    if(x<=tree[k].l&&tree[k].r<=y)
    {
        return tree[k];
    }
    int mid=(tree[k].l+tree[k].r)/2;
    if(y<=mid) return ask(k*2,x,y);
    else
    {
        if(x>mid) return ask(k*2+1,x,y);
        else
        {
            Tree t,a=ask(k*2,x,y),b=ask(k*2+1,x,y);
            t.maxleft=max(a.maxleft,a.sum+b.maxleft);
            t.maxright=max(b.maxright,a.maxright+b.sum);
            t.ans=max(max(a.ans,b.ans),a.maxright+b.maxleft);
            return t;
        }
    }
}

void change(int k,int x,int y)
{
    if(tree[k].l==tree[k].r)
    {
        tree[k].maxleft=tree[k].maxright=tree[k].ans=tree[k].sum=y;
        return;
    }
    int mid=(tree[k].l+tree[k].r)/2;
    if(x<=mid)change(k*2,x,y);
    else change(k*2+1,x,y);
    pushup(k);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;++ i)
    	scanf("%lld",&a[i]);
    build(1,n,1);
    while(m--)
    {
        int choose,x,y;
        scanf("%d%d%d",&choose,&x,&y);
        if(choose==1)
        {
            if(x>y)swap(x,y);
            printf("%lld\n",ask(1,x,y).ans);
        }
        else
        {
            change(1,x,y);
        }
    }
    return 0;
}

字典树

#include<bits/stdc++.h>
using namespace std;
int m,idx,son[100005][26],cnt[100005];
char op,str[100005];

void insert(char s[])
{
    int p = 0;  
    for(int i = 0; s[i]; i++)
    {
        int u = s[i] - 'a'; 
        if(!son[p][u]) son[p][u] = ++idx;   
        p = son[p][u];  
    }
    cnt[p]++;  
}

int query(char s[])
{
    int p = 0;
    for(int i = 0;s[i];i ++)
    {
        int u = s[i] - 'a';
        if(!son[p][u])return 0;
        else p = son[p][u];
    }
    return cnt[p];
}

int main()
{
    scanf("%d",&m);
    for(int i = 1;i <= m;i ++)
    {
        cin >> op >> str;
        if(op == 'I')insert(str);
        else printf("%d\n",query(str));
    }
    return 0;
}

莫队

/*小Z的袜子*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=50005;
int n,m,pos[maxn],c[maxn];
LL cnt[maxn],ans;
struct data{
    int l,r,id;
    LL a,b;
}a[maxn];
bool cmp(const data&a,const data&b)
{
    if(pos[a.l] == pos[b.l])
        return a.r < b.r;
    return a.l < b.l;
}
bool cmp2(const data&a,const data&b)
{
    return a.id < b.id;
}

void update(int p,int add){
    ans -= cnt[c[p]] * cnt[c[p]];
    cnt[c[p]] += add;
    ans += cnt[c[p]] * cnt[c[p]];
}

void solve()
{
    for(int i = 1,l = 1,r = 0;i <= m;i ++)
    {
        for(;r < a[i].r;r ++)
            update(r + 1,1);
        for(;r > a[i].r;r --)
            update(r, -1);
        for(;l < a[i].l;l ++)
            update(l,-1);
        for(;l > a[i].l;l --)
            update(l - 1,1);
        if(a[i].l==a[i].r)
		{
            a[i].a=0;
			a[i].b=1;
            continue;
        }
        a[i].a=ans-(a[i].r-a[i].l+1);
        a[i].b=(a[i].r-a[i].l+1)*1LL*(a[i].r-a[i].l);
        LL g=__gcd(a[i].a,a[i].b);
        a[i].a/=g;
		a[i].b/=g;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
		scanf("%d",&c[i]);
    int block=sqrt(n);
    for(int i=1;i<=n;i++)
		pos[i]=(i-1)/block+1;
    for(int i=1;i<=m;i++)
	{
        scanf("%d%d",&a[i].l,&a[i].r);
        a[i].id=i;
    }
    sort(a+1,a+m+1,cmp);
    solve();
    sort(a+1,a+m+1,cmp2);
    for(int i=1;i<=m;i++)
        printf("%lld/%lld\n",a[i].a,a[i].b);
    return 0;
}

树形DP

/*二叉苹果树*/
#include<bits/stdc++.h>
using namespace std;
int n,q;
struct Edge{
	int to,nxt,w;
}edge[1000];
int hd[200],idx,siz[205];
long long f[200][200];//根,留几条边 
void add(int x,int y,int z)
{
	edge[++ idx].to = y;edge[idx].nxt = hd[x];hd[x] = idx;
	edge[idx].w = z;
}
void dp(int u,int fa)
{
	for(int i = hd[u];i;i = edge[i].nxt)
	{
		int v = edge[i].to;
		if(v == fa)continue;
		dp(v,u);
		siz[u] += siz[v] + 1;
		for(int j = min(siz[u],q);j;j --)
		{
			for(int k = min(j - 1,siz[v]);k >= 0;k --)
			{
				f[u][j] = max(f[u][j],f[u][j - k - 1] + f[v][k]+ edge[i].w);
			}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&q);
	int x,y,z;
	for(int i = 1;i < n;i ++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	dp(1,0);
	printf("%lld",f[1][q]);
	return 0;
}

数位DP

/*NOIP2021数列*/
#include<bits/stdc++.h>
using namespace std;
#define rt register int
const int MAXN=35,MAXM=105,MOD=998244353;
int N,M,K;
int v[MAXM],powv[MAXM][MAXN],dp[MAXN][MAXN][MAXN][MAXN][MAXM],f[MAXN][MAXN][MAXN][MAXM];
int fac[MAXN],inv[MAXN];
inline int qpow(int x,int k)
{
    long long res=1,a=x;
	while(k){
		if(k&1) res=(long long)res*a%MOD;
		a=(long long)a*a%MOD;
		k>>=1;
	}
	return res;
}
inline void init(){
	fac[0]=1;
	for(rt i=1;i<=N;i++) fac[i]=1ll*fac[i-1]*i%MOD;
	inv[N]=qpow(fac[N],MOD-2);
	for(rt i=N-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%MOD;
}
inline int C(int n,int m){
	return 1ll*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
inline int popcnt(int x){
	int ret=0;
	while(x){
		if(x&1) ret++;
		x>>=1;
	}
	return ret;
}
int main(){
	scanf("%d%d%d",&N,&M,&K);
	init();
	for(rt i=0;i<=M;i++){
		scanf("%d",&v[i]);
		powv[i][0]=1;
		for(rt j=1;j<=N;j++) powv[i][j]=1ll*powv[i][j-1]*v[i]%MOD;
	}
	f[0][0][0][0]=1;
	for(rt p=0;p<=M;p++){
		for(rt i=0;i<=N;i++){
			for(rt j=0;j<=N-i;j++){
				for(rt k=0;k<=N;k++){
					for(rt num=0;num<=K;num++){
						if(num-(i+k)%2<0) continue;
						dp[i][j][k][num][p]=1ll*f[j][k][num-(i+k)%2][p]*powv[p][i]%MOD*C(N-j,i)%MOD;
						f[i+j][(i+k)/2][num][p+1]+=dp[i][j][k][num][p];
						f[i+j][(i+k)/2][num][p+1]%=MOD;
					}
				}
			}
		}
	}
	int ans=0;
	for(rt i=0;i<=N;i++){
		for(rt k=0;k<=N;k++){
			for(rt num=0;num<=K;num++){
				if(num+popcnt((i+k)/2)<=K) ans=(ans+dp[i][N-i][k][num][M])%MOD;
			}
		}
	}
	printf("%d",ans);
	return 0;
}

状态压缩

/*NOIP2016愤怒的小鸟*/
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

#define x first
#define y second

using namespace std;

typedef pair<double, double> PDD;

const int N = 18, M = 1 << 18;
const double eps = 1e-8;

int n, m;
PDD q[N];
int path[N][N];
int f[M];

int cmp(double x, double y)
{
    if (fabs(x - y) < eps) return 0;
    if (x < y) return -1;
    return 1;
}

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        cin >> n >> m;
        for (int i = 0; i < n; i ++ ) cin >> q[i].x >> q[i].y;

        memset(path, 0, sizeof path);
        for (int i = 0; i < n; i ++ )
        {
            path[i][i] = 1 << i;
            for (int j = 0; j < n; j ++ )
            {
                double x1 = q[i].x, y1 = q[i].y;
                double x2 = q[j].x, y2 = q[j].y;
                if (!cmp(x1, x2)) continue;
                double a = (y1 / x1 - y2 / x2) / (x1 - x2);
                double b = y1 / x1 - a * x1;

                if (cmp(a, 0) >= 0) continue;
                int state = 0;
                for (int k = 0; k < n; k ++ )
                {
                    double x = q[k].x, y = q[k].y;
                    if (!cmp(a * x * x + b * x, y)) state += 1 << k;
                }
                path[i][j] = state;
            }
        }

        memset(f, 0x3f, sizeof f);
        f[0] = 0;
        for (int i = 0; i + 1 < 1 << n; i ++ )
        {
            int x = 0;
            for (int j = 0; j < n; j ++ )
                if (!(i >> j & 1))
                {
                    x = j;
                    break;
                }

            for (int j = 0; j < n; j ++ )
                f[i | path[x][j]] = min(f[i | path[x][j]], f[i] + 1);
        }

        cout << f[(1 << n) - 1] << endl;
    }

    return 0;
}

单调队列DP

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
int n, m;
LL s[N], que[N];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%lld", &s[i]), s[i] += s[i - 1];
    LL res = -1e18;
    int hh = 0, tt = 0; que[0] = 0;
    for (int i = 1; i <= n; i ++ )
    {
        while (hh <= tt && i - que[hh] > m) hh ++ ;
        res = max(res, s[i] - s[que[hh]]);
        while (hh <= tt && s[que[tt]] >= s[i]) tt -- ;
        que[ ++ tt] = i;
    }
    printf("%lld\n", res);
    return 0;
}
posted @ 2022-10-11 21:14  zyc_xianyu  阅读(157)  评论(0编辑  收藏  举报