zoukankan      html  css  js  c++  java
  • 【模板复习+重构( 重 新 开 始 )记录+注解】+【错误记录】

    目录(未完善)

    1.快速幂||取余运算
    2.线段树1(区间加)
    3.gcd
    4.树状数组_单点加+区间查询
    5.树链剖分
    6.单调队列
    7.单调栈
    8.Dijkstra(无堆优化+有堆优化)
    9.Tarjan缩点
    10.二分

    快速幂_取余

    快速幂_取余运算
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    long long mod;
    inline long long pow_(long long a,long long b)
    {
    	long long ans=1,base=a;
    	while (b)
    	{
    		if (b&1)//拆解b的当前计算位
    		{
    			ans*=base;//(base是a的2的当前计算位-1次幂次幂)
    			ans%=mod;
    		}
    		base*=base;//(base^2)
    		base%=mod;
    		b>>=1;
    	}
    	return ans;
    }
    
    int main()
    {
    	long long n,m;
    	scanf("%lld%lld%lld",&n,&m,&mod);
    	printf("%lld^%lld mod %lld=%lld
    ",n,m,mod,pow_(n,m));
    }
    

    (tips:long tmd)


    线段树_区间加

    线段树_区间加
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(1e5+233) 
    long long ans[MAXN<<2],tag[MAXN<<2];
    int n,m;
    #define leftson cur<<1
    #define rightson cur<<1|1
    #define mid ((l+r)>>1)
    #define len (r-l+1)
    #define push_up ans[cur]=ans[leftson]+ans[rightson]
    #define push_down down(leftson,l,mid,tag[cur]); down(rightson,mid+1,r,tag[cur]); tag[cur]=0//其实这里似乎应该加一个if (tag[cur]!=0)(?)
    void build(int cur,int l,int r)
    {
    	if (l==r)
    	{
    		scanf("%lld",&ans[cur]);
    		return;
    	}
    	build(leftson,l,mid);
    	build(rightson,mid+1,r);
    	push_up;
    }
    inline void down(int cur,int l,int r,long long k)
    {
    	ans[cur]+=(len*k);
    	tag[cur]+=k;
    	return;
    }
    void add(int cur,int l,int r,int cl,int cr,long long k)
    {
    	if (cl<=l&&r<=cr)
    	{
    		ans[cur]+=(k*len);
    		tag[cur]+=k;
    		return;
    	}
    	push_down;//节点标记下传
    	if (cl<=mid) add(leftson,l,mid,cl,cr,k);//修改区间包含左子树则操作
    	if (cr>mid) add(rightson,mid+1,r,cl,cr,k);//修改区间包含右子树则操作
    	push_up;//上传
    }
    long long query(int cur,int l,int r,int ql,int qr)
    {
    	if (ql<=l&&r<=qr) return ans[cur];
    	push_down;
    	long long answ=0;
    	if (ql<=mid) answ+=query(leftson,l,mid,ql,qr);
    	if (qr>mid) answ+=query(rightson,mid+1,r,ql,qr);
    	push_up;
    	return answ;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	build(1,1,n);
    	int opt,x,y;
    	long long k;
    	while(m--)
    	{
    		scanf("%d%d%d",&opt,&x,&y);
    		if (opt==1)
    		{
    			scanf("%lld",&k);
    			add(1,1,n,x,y,k);
    		}
    		else printf("%lld
    ",query(1,1,n,x,y));
    	}
    	return 0;
    }
    
    >(tips:傻逼才会把len mid k写乱位置,我是傻逼)//QAQ 我也会( >(tips:n*m的数组for 1 to n for 1 to n 是傻逼)

    线段树2_区间乘_区间加

    这份代码是我初一的时候写的。有时间再来重构一下吧
    话说最近重构了好多板子啊
    不过我以前确实码风有点丑

    线段树2_区间乘_区间加
    #include <algorithm>
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #define ll long long
    #define MAXN 200002<<1
    #define leftson cur<<1
    #define rightson (cur<<1|1)
    #define mid ((l+r)>>1)
    #define push_up ans[cur]=ans[leftson]+ans[rightson]
    //#define push_down downmul(cur); downadd(cur,l,r)
    #define push_down pushdown(cur);
    using namespace std;
    ll ans[MAXN],add[MAXN]={},mul[MAXN],lefts[MAXN],rights[MAXN];
    ll n,m,mod;
    ll a,b,c;
    void build(ll cur,ll l,ll r)
    {
    	mul[cur]=1;
    	add[cur]=0;
    	lefts[cur]=l;
    	rights[cur]=r;
        if (l==r)
        {
            scanf("%lld",&ans[cur]);
            return;
        }
        build(leftson,l,mid);
        build(rightson,mid+1,r);
        push_up;
    }
    /*
    inline void downmul(ll cur)
    {
        if (mul[cur]!=1)
        {
            ans[leftson]=(ans[leftson]*mul[cur])%mod;
            ans[rightson]=(ans[rightson]*mul[cur])%mod;
            mul[leftson]=(mul[leftson]*mul[cur])%mod;
            mul[rightson]=(mul[rightson]*mul[cur])%mod;
            add[leftson]=(add[leftson]*mul[cur])%mod;
            add[rightson]=(add[rightson]*mul[cur])%mod;
            mul[cur]=1;
        }
    }
    inline void downadd(ll cur,ll l,ll r)
    {
        ll ln=(mid-l+1),rn=(r-mid);
        if (add[cur]!=0)
        {
            ans[leftson]=(ans[leftson]+add[cur]*ln)%mod;
            ans[rightson]=(ans[rightson]+add[cur]*rn)%mod;
            add[cur]=0;
        }
    }
    *///好像没用到
    inline void pushdown(ll cur)
    {
    	ans[leftson]=(ans[leftson]*mul[cur]+add[cur]*(rights[leftson]-lefts[leftson]+1))%mod;//大概是想写 ans[cur]=ans[cur]*kmul+len*kadd
    	ans[rightson]=(ans[rightson]*mul[cur]+add[cur]*(rights[rightson]-lefts[rightson]+1))%mod;
    	mul[leftson]=(mul[leftson]*mul[cur])%mod;//  mul[cur]*=kmul;
    	mul[rightson]=(mul[rightson]*mul[cur])%mod;
    	add[leftson]=(add[leftson]*mul[cur]+add[cur])%mod;//  add[cur]=add[cur]*kmul+kadd;
    	add[rightson]=(add[rightson]*mul[cur]+add[cur])%mod;
    	mul[cur]=1;//清空掉父亲的标记
    	add[cur]=0;
    }
    inline void mulchange(ll mu,ll mur,ll cur,ll l,ll r,ll delta)
    {
        if (mu<=l&&r<=mur)
        {
            ans[cur]=ans[cur]*delta%mod;
            mul[cur]=mul[cur]*delta%mod;
            add[cur]=add[cur]*delta%mod;
            return;
        }
        push_down;
        if (mu<=mid) mulchange(mu,mur,leftson,l,mid,delta);
        if (mur>mid) mulchange(mu,mur,rightson,mid+1,r,delta);
        push_up;
    }
    inline void addchange(ll adl,ll adr,ll cur,ll l,ll r,ll delta)
    {
        if (adl<=l&&r<=adr)
        {
            ans[cur]+=delta*(r-l+1);
            add[cur]+=delta;
            return;
        }
        push_down;
        if (adl<=mid) addchange(adl,adr,leftson,l,mid,delta);
        if (adr>mid) addchange(adl,adr,rightson,mid+1,r,delta);
        push_up;
    }
    ll query(ll quel,ll quer,ll cur,ll l,ll r)
    {
        if (quel<=l&&r<=quer)
        {
            return ans[cur];
        }
        push_down;
        ll answer=0;
        if (quel<=mid) answer+=query(quel,quer,leftson,l,mid);
        if (quer>mid) answer+=query(quel,quer,rightson,mid+1,r);
        return answer;
    }
    int main()
    {
        scanf("%lld%lld%lld",&n,&m,&mod);
        build(1,1,n);
        ll cs;
        for (ll i=1;i<=m;i++)
        {
            scanf("%lld",&cs);
            if (cs==1)
            {
                scanf("%lld%lld%lld",&a,&b,&c);
                mulchange(a,b,1,1,n,c);
                continue;
            }
            if (cs==2)
            {
                scanf("%lld%lld%lld",&a,&b,&c);
                addchange(a,b,1,1,n,c);
                continue;
            }
            scanf("%lld%lld",&a,&b);
            printf("%lld
    ",query(a,b,1,1,n)%mod);
        }
        return 0;
    }
    

    gcd

    gcd
    int gcd(int a,int b)
    {
    	if (b==0) return a;
    	return gcd(b,a%b);
    }
    
    

    树状数组_单点加_区间查询

    附一个lowbit的意义:非负整数n在二进制表示下,最低位的1及后边所有的0构成的数值。例如(lowbit((1001100)_2)=(100)_2)

    树状数组_单点加_区间查询
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(5e5+233)
    int n,m;
    int c[MAXN];
    #define lowbit(a) (a&-a)
    inline void add(int x,int k)
    {
    	while (x<=n)
    	{
    		c[x]+=k;
    		x+=lowbit(x);
    	}
    }
    inline int query(int x)
    {
    	int sum=0;
    	while (x>0)
    	{
    		sum+=c[x];
    		x-=lowbit(x);
    	}
    	return sum;
    }
    inline int solve(int x,int y) { return query(y)-query(x-1); }//如果有进行模运算这里应该需要注意一下...
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1,k;i<=n;i++)
    	{
    		scanf("%d",&k);
    		add(i,k);
    	}
    	int opt,x,y;
    	while (m--)
    	{
    		scanf("%d%d%d",&opt,&x,&y);
    		if (opt==1) add(x,y);
    		else printf("%d
    ",solve(x,y));
    	}
    }
    

    (tips:void return int的sb()

    (tips:减模小心爆负)

    (tips:线段树开四倍数组((


    树链剖分

    树链剖分
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(1e5+233)
    struct qwq
    {
    	int nex,to;
    }e[MAXN<<1];
    int h[MAXN],tot=0;
    int n,m,r;
    long long mod;
    inline void add(int x,int y)
    {
    	e[++tot].nex=h[x];
    	e[tot].to=y;
    	h[x]=tot;
    }
    long long a[MAXN],a2[MAXN];
    //seg_tree
    long long ans[MAXN<<2],tag[MAXN<<2];
    #define leftson cur<<1
    #define rightson cur<<1|1
    #define mid ((l+r)>>1)
    #define len (r-l+1)
    #define push_up ans[cur]=ans[leftson]+ans[rightson]
    #define push_down down(leftson,l,mid,tag[cur]); down(rightson,mid+1,r,tag[cur]); tag[cur]=0;
    //线段树部分
    inline void down(int cur,int l,int r,long long k)
    {
    	ans[cur]+=((len*k)%mod);
    	tag[cur]+=k;
    	ans[cur]%=mod;
    	tag[cur]%=mod;
    }
    void build(int cur,int l,int r)
    {
    	if (l==r)
    	{
    		ans[cur]=a2[l];//建树使用新dfs序编号
    		return;
    	}
    	build(leftson,l,mid);
    	build(rightson,mid+1,r);
    	push_up;
    }
    void change(int cur,int l,int r,int cl,int cr,long long k)
    {
    	if (cl<=l&&r<=cr)
    	{
    		ans[cur]+=((len*k)%mod);
    		tag[cur]+=k;
    		ans[cur]%=mod;
    		tag[cur]%=mod;
    		return;//main开头的调试甚至是因为这里没写return()
    	}
    	push_down;
    	if (cl<=mid) change(leftson,l,mid,cl,cr,k);
    	if (cr>mid) change(rightson,mid+1,r,cl,cr,k);
    	push_up;
    }
    long long query(int cur,int l,int r,int ql,int qr)
    {
    	if (ql<=l&&r<=qr) return ans[cur]%mod;
    	push_down;
    	long long answ=0;//不要和线段树数组重名!!!!!!!!!!!!!!!!!!!!!!!wtm错了好多次
    	if (ql<=mid) answ+=query(leftson,l,mid,ql,qr)%mod;
    	if (qr>mid) answ+=query(rightson,mid+1,r,ql,qr)%mod;
    	push_up;
    	return answ%mod;
    }
    
    //tree
    int id[MAXN],son[MAXN],dep[MAXN],fa[MAXN],siz[MAXN],top[MAXN];//父亲,深度,子树大小,重儿子,重链顶,树剖dfs序编号
    //init
    void dfs_1(int x)
    {
    	siz[x]=1;//子树要先计算本身
    	int maxn=-1;
    	for (int i=h[x],y;i;i=e[i].nex)
    	{
    		y=e[i].to;
    		if (y==fa[x]) continue;
    		fa[y]=x;
    		dep[y]=dep[x]+1;
    		dfs_1(y);
    		siz[x]+=siz[y];//愿世间没有____=____+1
    		if (siz[y]>maxn)//子树最大的儿子为重儿子
    		{
    			son[x]=y;
    			maxn=siz[y];
    		}
    	}
    }
    int cnt=0;
    void dfs_2(int x,int tp)
    {
    	id[x]=++cnt;
    	a2[cnt]=a[x];
    	top[x]=tp;//重链顶
    	if (!son[x]) return;//重链由重儿子延续
    	dfs_2(son[x],tp);
    	for (int i=h[x],y;i;i=e[i].nex)
    	{
    		y=e[i].to;
    		if (y==fa[x]||y==son[x]) continue;
    		dfs_2(y,y);//轻儿子作为新重链顶
    	}
    }
    
    //add list
    inline void add_list(int x,int y,long long k)
    {
    	while (top[x]!=top[y])//当x,y不在同一条链上,进行跳链
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);//更新为x相对y所在链顶端深度较低
    		change(1,1,n,id[top[x]],id[x],k);//对x所在的链中被需操作链包含的部分操作,即当前x所在重链由x到重链顶 
    		x=fa[top[x]];//x跳至当前重链顶
    	}
    	if (dep[x]>dep[y]) swap(x,y);
    	change(1,1,n,id[x],id[y],k);//x与y在同一条链上,则编号连续,直接进行操作
    	return;
    }
    //query list
    inline long long query_list(int x,int y)
    {
    	long long answ=0;
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		answ+=query(1,1,n,id[top[x]],id[x]);
    		answ%=mod;
    		x=fa[top[x]];
    	}
    	if (dep[x]>dep[y]) swap(x,y);
    	answ+=query(1,1,n,id[x],id[y]);
    	return answ%mod;
    }
    //add tree
    inline void tree_add(int x,long long k)
    {
    	change(1,1,n,id[x],id[x]+siz[x]-1,k);//树链剖分所得的dfn本身是一种dfs序,子树由id[x]开始编号连续。
    }
    //query tree
    inline long long tree_query(int x)
    {
    	return query(1,1,n,id[x],id[x]+siz[x]-1);
    }
    int main()
    {
    	scanf("%d%d%d%lld",&n,&m,&r,&mod);
    	/*
    	change(1,1,n,4,6,3);
    	change(1,1,n,3,5,7);
    	cout<<query(1,1,n,3,4)<<endl;//拿来调试的,丑(
    	*/
    	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    	for (int i=1,x,y;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	dfs_1(r);
    	dfs_2(r,r);
    	build(1,1,n);
    	/*
    	puts("uwu");
    	for (int i=1;i<=n;i++) printf("%d ",id[i]);
    	puts("w");*/
    	int opt;
    	int x,y;
    	long long z;
    	while (m--)
    	{
    		scanf("%d",&opt);
    		if (opt==1)
    		{
    			scanf("%d%d%lld",&x,&y,&z);
    			add_list(x,y,z);
    		}
    		else if (opt==2)
    		{
    			scanf("%d%d",&x,&y);
    			printf("%lld
    ",query_list(x,y));
    		}
    		else if (opt==3)
    		{
    			scanf("%d%lld",&x,&z);
    			tree_add(x,z);
    		}
    		else
    		{
    			scanf("%d",&x);
    			printf("%lld
    ",tree_query(x));
    		}
    	}
    	return 0;
    }
    

    单调队列

    单调队列
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(1e6+233) 
    #include <queue>
    struct qwq
    {
    	int id,w;
    };
    int a[MAXN];
    deque<qwq> q;
    int main()
    {
    	int n,k;
    	scanf("%d%d",&n,&k);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for (int i=1;i<=n;i++)//求长度为k窗口最小值,队列内元素单调递增
    	{
    		while (!q.empty()&&q.back().w>=a[i]) q.pop_back();//出现更小元素时,前面的小元素就过期(不可能再成为最小值)了,从队尾出队。
    		q.push_back((qwq){i,a[i]});
    		while (!q.empty()&&q.front().id<(i-k+1)) q.pop_front();//队列中元素出现时间单调递增,过期元素从队头出队
    		if (i>=k) printf("%d ",q.front().w);
    	}
    	puts("");
    	q.clear();
    	for (int i=1;i<=n;i++)
    	{
    		while (!q.empty()&&q.back().w<=a[i]) q.pop_back();
    		q.push_back((qwq){i,a[i]});
    		while (!q.empty()&&q.front().id<(i-k+1)) q.pop_front();
    		if (i>=k) printf("%d ",q.front().w);
    	}
    	return 0;
    }
    

    单调栈

    单调栈
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #include <stack>
    int n;
    #define MAXN (int)(3e6+233)
    stack<int> st;
    int a[MAXN],r[MAXN];
    int main()
    {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for (int i=1;i<=n;i++)
    	{
    		while (!st.empty()&&a[st.top()]<a[i])//求每个数右边第一个比其大的数的下标,栈内元素由底到顶单调递减。当入栈不满足单调性时持续出栈
    		{
    			r[st.top()]=i;//出栈过程即可标记
    			st.pop();
    		}
    		st.push(i);
    	}
    	for (int i=1;i<=n;i++) printf("%d ",r[i]); puts("");
    	return 0;
    }
    

    dijkstra

    dijkstra
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN 2510
    #define MAXM 6200
    int n,m,s,t;
    #define inf (int)(1e9+300)
    struct qwq
    {
    	int nex,to,w;
    }e[MAXM<<1];
    int h[MAXN],tot=0;
    inline void add(int x,int y,int z)
    {
    	e[++tot].nex=h[x];
    	e[tot].to=y;
    	h[x]=tot;
    	e[tot].w=z;
    }
    
    int vis[MAXN],dis[MAXN];
    
    inline void dijkstra()
    {
    	int x=s;
    	vis[x]=1;//设源点为已遍历
    	dis[x]=0;//源点到本身最短路为0
    	int summmm=n-1,minn=inf;
    	while (summmm--)//执行n-1次
    	{
    		for (int i=h[x],y;i;i=e[i].nex)
    		{
    			y=e[i].to;
    			if (dis[y]>dis[x]+e[i].w)
    				dis[y]=dis[x]+e[i].w;
    		}
    		minn=inf;
    		for (int i=1;i<=n;i++)
    		{
    			if (!vis[i]&&dis[i]<minn)//找出未遍历的未更新答案的节点
    			{
    				minn=dis[i];
    				x=i;
    			}
    		}
    		vis[x]=1;//将新的预遍历节点标记
    	}
    }
    
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&s,&t);
    	for (int i=1,u,v,w;i<=m;i++)
    	{
    		scanf("%d%d%d",&u,&v,&w);
    		add(u,v,w);
    		add(v,u,w);
    	}
    	for (int i=1;i<=n;i++) dis[i]=inf;
    	dijkstra();
    	printf("%d
    ",dis[t]);
    }
    

    Dijkstra_priority_queue_optimized

    Dijkstra_priority_queue_optimized
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <queue>
    using namespace std;
    int n,m,s;
    #define MAXN (int)(1e5+233)
    #define MAXM (int)(2e5+233) 
    struct qwq
    {
    	int nex,to,w;
    }e[MAXM];
    int h[MAXN],tot=0;
    inline void add(int x,int y,int z)
    {
    	e[++tot].to=y;
    	e[tot].w=z;
    	e[tot].nex=h[x];
    	h[x]=tot;
    }
    bool vis[MAXN];
    int dis[MAXN];
    struct Node { int id,diss; };
    bool operator < (const Node &a,const Node &b) { return a.diss>b.diss; }
    priority_queue<Node> q;
    inline void dijkstra()
    {
    	int x;
    	q.push((Node){s,0});//先把起点塞进优先队列里
    	dis[s]=0;
    	while (!q.empty())
    	{
    		x=q.top().id; q.pop();
    		if (vis[x]) continue;//访问过了就跳过。
    		vis[x]=true;//check in!
    		for (int i=h[x],y;i;i=e[i].nex)
    		{
    			y=e[i].to;
    			if (dis[y]>dis[x]+e[i].w)
    			{
    				dis[y]=dis[x]+e[i].w;//更新
    				if (!vis[y])
    					q.push((Node){y,dis[y]});//未访问过就塞进去
    			}
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&s);
    	for (int i=1,x,y,z;i<=m;i++)
    	{
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z);
    	}
    	for (int i=1;i<=n;i++) dis[i]=(int)(2e9+7000);
    	dijkstra();
    	for (int i=1;i<=n;i++) printf("%d ",dis[i]);
    	return 0;
    }
    

    (tips:stl的priority_queue默认是大根堆。写转义运算符的时候要把符号反过来)

    (tips:以及....初始化dis。)


    缩点

    yysy,这里好像有个点双边双连通分量板子....

    https://www.luogu.com.cn/team/21355#problem

    无向图缩点
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(1e4+233)
    #define MAXM (int)(1e5+233)
    #include <stack>
    int a[MAXN];
    stack<int> st;
    struct qwq
    {
    	int nex,to;
    }e[MAXM],e2[MAXM];
    int h[MAXN],h2[MAXN],tot=0,tot2=0;
    inline void add(int x,int y)
    {
    	e[++tot].to=y;
    	e[tot].nex=h[x];
    	h[x]=tot;
    }
    bool vis[MAXN];
    int dfn[MAXN],lst[MAXN],cnt=0;
    int color[MAXN],c_count=0;
    int w[MAXN];
    void tarjan(int x)
    {
    	vis[x]=true;//vis实际上表示该节点是否在栈中,此处标记x入栈 
    	st.push(x);//x入栈 
    	dfn[x]=lst[x]=++cnt;
    	for (int i=h[x],y;i;i=e[i].nex)
    	{
    		y=e[i].to;
    		if (!dfn[y]) { tarjan(y); lst[x]=min(lst[x],lst[y]); }//y未遍历过,(x,y)为树枝边 
    		else if (vis[y]) lst[x]=min(lst[x],dfn[y]);//y在栈内 
    	}
    	if (dfn[x]==lst[x])//x为x所在的连通分量的根 
    	{
    		color[x]=++c_count;//对x染色 
    		vis[x]=false;//出栈标记 
    		w[c_count]+=a[x];//统计分量点权值和 
    		int y;
    		while (st.top()!=x)
    		{
    			y=st.top();
    			color[y]=c_count;//对连通分量中的点染色 
    			vis[y]=false;//出栈标记 
    			w[c_count]+=a[y];//统计分量点权值和 
    			st.pop();
    		}
    		st.pop();
    	}
    }
    int in[MAXN];
    inline void add2(int x,int y)
    {
    	in[y]++;
    	e2[++tot2].to=y;
    	e2[tot2].nex=h2[x];
    	h2[x]=tot2; 
    }
    #include <queue>
    queue<int> q;
    int f[MAXN];
    int main()
    {
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for (int i=1,x,y;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 x=1;x<=n;x++)
    		for (int i=h[x];i;i=e[i].nex) 
    			if (color[x]!=color[e[i].to]) add2(color[x],color[e[i].to]);//以连通分量为单位重新建图(记得写add2) 
    	//从这里开始使用图e2[] 
    	for (int i=1;i<=c_count;i++)//新图节点数是c_count 
    	{
    		if (!in[i]) q.push(i);
    		f[i]=w[i];
    	}
    	int x; 
    	while (!q.empty())//缩点后图形态为DAG。进行DAG上的最长路DP 
    	{
    		x=q.front();
    		q.pop();
    		for (int i=h2[x],y;i;i=e2[i].nex)
    		{
    			y=e2[i].to;
    			in[y]--;
    			f[y]=max(f[y],f[x]+w[y]);
    			if (!in[y]) q.push(y);
    		}
    	}
    	int ans=-1;
    	for (int i=1;i<=n;i++) ans=max(ans,f[i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    https://www.luogu.com.cn/record/59925909

    无向图上求双连通分量,先求出桥并标记再dfs染色。

    这个代码是模拟赛时的,桥的判定是lst[y]>dfn[x]

    边编号从2开始,相同边的反向边由异或1互相转换。

    无向图边双联通分量缩点

    边双缩点
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(5e4+233)
    #define MAXM (int)(5e4+233)
    struct qwq
    {
    	int nex,to;
    }e[MAXM<<1];
    int h[MAXN],tot=1;
    int n,m;
    inline void add(int x,int y)
    {
    	e[++tot].nex=h[x];
    	e[tot].to=y;
    	h[x]=tot;
    }
    bool book[MAXM<<1];
    int dfn[MAXN],lst[MAXN],cnt=0;
    void tarjan(int x,int ie)
    {
    	dfn[x]=lst[x]=++cnt;
    	for (int i=h[x],y;i;i=e[i].nex)
    	{
    		y=e[i].to;
    		if (!dfn[y])
    		{
    			tarjan(y,i);
    			lst[x]=min(lst[x],lst[y]);
    			if (lst[y]>dfn[x]) book[i]=book[i^1]=true;//,printf("bridge: %d %d
    ",x,y);
    		}
    		else if (i!=(ie^1)) lst[x]=min(lst[x],dfn[y]);//不等于从父亲来的那条边。这样写可以处理重边
    	}
    }
    int color[MAXN],c_count=0;
    void dfs(int x)
    {
    	color[x]=c_count;
    	for (int i=h[x],y;i;i=e[i].nex)
    	{
    		y=e[i].to;
    		if (color[y]||book[i]) continue;
    		dfs(y);
    	}
    }
    int deg[MAXN];
    inline int tc(int x)
    {
    	if (x%2==0) return x/2;
    	return (int)(x*1.0/2.0)+1;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i,0);
    	for (int i=1;i<=n;i++)
    		if (!color[i]) { c_count++; dfs(i); }
    	for (int x=1;x<=n;x++)
    		for (int i=h[x],y;i;i=e[i].nex)
    		{
    			y=e[i].to;
    			if (color[x]!=color[y])
    				deg[color[y]]++;
    		}
    	int sum=0;
    	for (int i=1;i<=c_count;i++)
    		if (deg[i]==1) sum++;
    	printf("%d
    ",tc(sum));
    	return 0;
    }
    

    割点

    割点
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(2e4+233)
    #define MAXM (int)(1e5+233)
    struct qwq
    {
    	int nex,to;
    }e[MAXM<<1];
    int h[MAXN],tot=0;
    inline void add(int x,int y)
    {
    	e[++tot].to=y;
    	e[tot].nex=h[x];
    	h[x]=tot;
    }
    int dfn[MAXN],lst[MAXN];
    int id=0;
    bool cut[MAXN];
    int rt;
    void tarjan(int x)
    {
    	dfn[x]=lst[x]=++id;
    	int ssum=0;
    	for (int i=h[x],y;i;i=e[i].nex)
    	{
    		y=e[i].to;
    		if (!dfn[y])
    		{
    			tarjan(y);
    			lst[x]=min(lst[x],lst[y]);
    			if (lst[y]>=dfn[x]&&x!=rt) cut[x]=true;//割点判定
    			ssum++;
    		}
    		else lst[x]=min(lst[x],dfn[y]);
    	}
    	if (x==rt&&ssum>1) cut[rt]=true;//当前搜索树的root,须有至少两个子节点y满足lst[y]>=dfn[x]才为割点
    }
    int main()
    {
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y); add(y,x);
    	} 
    	for (int i=1;i<=n;i++)
    		if (!dfn[i]) { rt=i; tarjan(i);  }
    	int sum=0;
    	for (int i=1;i<=n;i++)
    		if (cut[i]) sum++;
    	printf("%d
    ",sum);
    	for (int i=1;i<=n;i++)
    		if (cut[i]) printf("%d ",i); puts("");
    	return 0;
    }
    

    写了个假的v-DCC做法之后发现一个很大的误区

    v-DCC的含义是极大不含割点的子图。一个割点可能被多个v-DCC包含

    一张无向图是点双连通图,当且仅当满足下列两个条件之一

    1.图的顶点数不超过2

    2.图中任意两点都同时包含在至少一个简单环中。(属于两个不同点双连通分量的两个节点,不可能同时包含在至少一个简单环中)

    书中其实还有个引理()若某个v-DCC中存在奇环,则这个v-DCC中的所有点都被至少一个奇环包含。很好证,不写了(

    求点双过程写代码注释里了(

    点双连通分量
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(5e4+233)
    #define MAXM (int)(1e5+233)
    int n,m;
    struct qwq
    {
    	int nex,to;
    }e[MAXM<<1];
    int h[MAXN],tot=0;
    inline void add(int x,int y)
    {
    	e[++tot].to=y;
    	e[tot].nex=h[x];
    	h[x]=tot;
    }
    #include <stack>
    #include <vector>
    stack<int> st;
    int dfn[MAXN],lst[MAXN],cnt=0;
    bool Cut[MAXN];
    int rt;
    vector<int> v[MAXN];
    int vcnt=0;
    void Tarjan(int x)
    {
    	dfn[x]=lst[x]=++cnt;
    	st.push(x);//1.当一个节点第一次被访问,将该节点入栈。
    	int ssum=0;
    	if (x==rt&&(!h[x])) { v[++vcnt].push_back(x); return; }
    	for (int i=h[x],y;i;i=e[i].nex)
    	{
    		y=e[i].to;
    		if (!dfn[y])
    		{
    			Tarjan(y);
    			lst[x]=min(lst[x],lst[y]);
    			if (lst[y]>=dfn[x])//2.当割点判定成立(x为割点时)
    			{
    				if (x!=rt)
    					Cut[x]=true;//这里还顺便求了割点来着....似乎是不用求的(?
    				else
    					ssum++;
    				//从栈顶不断弹出节点直至y被弹出
    				int z;
    				vcnt++;
    				do
    				{
    					z=st.top();
    					st.pop();
    					v[vcnt].push_back(z);
    				} while (z!=y);
    				v[vcnt].push_back(x);//刚刚弹出的所有节点与x一起构成了一个v-DDC。
    			}
    		}
    		else lst[x]=min(lst[x],dfn[y]);
    	}
    	if (x==rt&&ssum>=2) Cut[x]=true;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	for (int i=1;i<=n;i++)
    		if (!dfn[i])
    		{
    			rt=i;
    			Tarjan(i);
    		}
    //	for (int i=1;i<=n;i++) printf("%d ",Cut[i]); puts("w");
    	for (int i=1,len;i<=vcnt;i++)
    	{
    		len=v[i].size();
    		for (int j=0;j<len;j++)
    			printf("%d ",v[i][j]);
    		printf("
    ");
    	}
    	return 0;
    }
    

    然后就是点双缩点了

    方法是先给每个割点新标一个号,再将每个v-DDC与它包含的所有割点连边。

    代码暂时没有()


    二分

    1.整数区间上二分
    (I)查找最小的答案
    Code
    inline void binary_search(int l,int r)
    {
    	#define mid ((l+r)>>1)
    	while (l<r)
    		if (check(mid))
    			r=mid;
    		else l=mid+1;
    	return l;
    }
    
    (II)查找最大的答案
    Code
    inline void binary_search(int l,int r)
    {
    	#define mid ((l+r+1)>>1)
    	while (l<r)
    		if (check(mid))
    			l=mid;
    		else r=mid-1;
    	return l;
    }
    
    (III)实数域二分
    Code
    while (l+eps<r)
    {
    	double mid=(l+r)/2;
    	if (check(mid)) r=mid; else l=mid;
    }
    

    其中eps为精度。若要保留k位小数,则取(eps=10^{-(k+2)})

    乘法逆元

    差点忘了。

    (adiv b mod p = a*b^{p-2} mod p)

    嗯....似乎还要复习一下基本的dp什么的。相当生疏了啊

    二分图判定

    .....怎么会有这个东西。。。。。。。。。。。。。。。

    一张无向图是二分图,当且仅当图中不存在长度为奇数的环。

    黑白染色即可。一个节点的相邻节点应与其染色相反,若染色过程产生冲突,则存在奇环。

    所以反过来二分图判定也可以用来判奇环()

    (tips:注意题目是否给出树根)

    (tips:longlong!!!!!!!!longlong!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!)

    (tips:出现一个非0数到了另一个地方变成0了,可能是因为int a=(long long)了)


    SPFA求负环

    SPFA
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    #define MAXN (int)(2e4+233)
    #define MAXM (int)(6e4+233)
    struct qwq
    {
    	int nex,to,w;
    }e[MAXM];
    int h[MAXN],tot=0;
    inline void add(int x,int y,int z)
    {
    	e[++tot].to=y;
    	e[tot].nex=h[x];
    	e[tot].w=z;
    	h[x]=tot;
    }
    #define inf (long long)(2e11+233)
    long long dis[MAXN];
    int cnt[MAXN];
    bool vis[MAXN];
    int n,m;
    inline void INIT()
    {
    	for (int i=1;i<=n;i++) dis[i]=inf,vis[i]=0,cnt[i]=0;
    }
    #include <queue>
    queue<int> q;
    int s=1;
    bool spfa()
    {
    	scanf("%d%d",&n,&m);
    	//scanf
    	INIT();
    	//INIT
    	for (int i=1,x,y,z;i<=m;i++)
    	{
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z);
    		if (z>=0) add(y,x,z);
    	}
    	dis[s]=0; vis[s]=true;//入队标记
    	q.push(s);
    	int x;
    	while (!q.empty())
    	{
    
    		x=q.front();
    		q.pop();
    		vis[x]=false;//出队标记
    		// printf("x:%d hx:%d
    ",x,h[x]);
    		for (int i=h[x],y;i;i=e[i].nex)
    		{
    			y=e[i].to;
    			if (dis[y]>dis[x]+e[i].w)
    			{
    				dis[y]=dis[x]+e[i].w;
    				if (!vis[y])
    				{
    					cnt[y]=cnt[x]+1;
    					if (cnt[y]>=n) return true;//
    					q.push(y);
    					vis[y]=true;//入队标记
    				}
    			}
    		}
    	}
    	return false;//.......nmd
    }
    int main()
    {
    	int T;
    	scanf("%d",&T);
    	while (T--)
    	{
    		if (spfa()) puts("YES");
    		else puts("NO");
    		//edge clear
    		for (int i=1;i<=n;i++) h[i]=0; tot=0;
    	}
    	return 0;
    }
    

    (tips:多测要清空....queue?)

    (tips:喂!!!!怎么能(int)(1e16))

    (tips:爆负了就从头到尾检查取模啊!!!!!!!!)

    (tips:认真检查删调试!!)

    (tips:别tm忘打大括号啊!!!!!!!!!)

    (tips:l o n g l o n g !!!!!!!!!!!!!!!!!!!!!!下次能开就开算了()但是按照自己的做事方式似乎很难允许自己这么干)


    带权并查集

    在并查集的基础上对节点到父亲的边加了边权...... 路径压缩的时候就直接计算节点到根的边权和。

    并查集还是带权并查集也好,都维护具有传递性的关系

    放个例题:[ARC090B] People on a Line

    给定(m)组信息,第(i)组信息形如((l_i,r_i,d_i)),含义为(x_{r_i}-x_{l_i}=d_i)。 判断是否所有信息都不冲突

    这题原题面花里胡哨的,其实跟另一个带权并查集板子是一样的()那题是给出一系列区间和判断是否有冲突,实质上也是给出一组(a_{r_i}-a_{l_i}=d_i)

    对于通过添加(root_r)(root_l)关系的话,我画了个图()虽然感觉有点冗余

    这里用(fr)表示(root_r)(fl)表示(root_l)

    原先的情况

    现在要建立一个r到l的关系d

    怎么通过dis'[fr]来建立?

    使得dis[l]+d=dis[r]+dis'[fr],也即dis'[fr]=dis[l]+d-dis[r]

    没了(

    带权并查集
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define MAXN (int)(1e5+233)
    using namespace std;
    int n,m;
    int dis[MAXN],fa[MAXN];
    inline void INIT() { for (int i=1;i<=n;i++) fa[i]=i; }
    int found(int x)
    {
    	if (x==fa[x]) return x;
    	int rt=found(fa[x]);//先找root
    //	printf("x: %d   fa: %d
    ",x,fa[x]);
    	dis[x]+=dis[fa[x]];//再更新在当前root下的前缀和。本来在想这么写为什么正确,然后发现并查集每次合并并且运行found后森林一直是一堆菊花图的形态
    	return fa[x]=rt;//路径压缩
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	INIT();
    	for (int i=1,l,r,d,fl,fr;i<=m;i++)
    	{
    		scanf("%d%d%d",&l,&r,&d);
    		fl=found(l); fr=found(r);
    //		for (int j=1;j<=n;j++) printf("%d %d
    ",fa[j],dis[j]); puts("fA!");
    		if (fl!=fr)
    		{
    			dis[fr]=d+dis[l]-dis[r];
    			fa[fr]=fl;
    		}
    		else if (dis[r]-dis[l]!=d)//fl==fr,l与r在同个并查集中,也即l与r已经存在了联系。dis[i]表示r在[root[i]~i]区间的前缀和,即dis[r]-dis[l]可以表示l,r已经存在的这组联系值(换到区间和和那题更好表述为l+1到r的区间和)。
    		{
    			puts("No");
    			return 0;
    		}
    	}
    	puts("Yes");
    	return 0;
    }
    

    https://www.luogu.com.cn/blog/wey-yzyl/zui-tai-zi-duan-hu-ji-ji-bian-shi-di-qi-shi

    chy分享给我们的最大子段和变式及解法整理()

    (tips:线段树维护RMQ什么的...push_up记得写手写long long max/min函数啊)

    (tips:别手瓢就tm(int)(1e18)啊!!!就算写#define mod (long long)(1e9+7)也还是别int...)


    李超树

    把 [HEOI2013] Segment 当板子写了

    本来在想先写哪道()Segment 是加线段,好像那道 JSOI 的是加直线,并且直线斜率大于0..

    最后两个都写了。yysy,题解的码风各异....花了好多时间大致统一出了一个比较好看的写法。

    [HEOI2013] Segment
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define MAXN (int)(1e5+233)
    #define MAXXK (39989)
    #define MAXK (int)(4e4+233)
    #define MAXY (int)(1e9+233)
    const double EPS=1e-8;
    using namespace std;
    int OPT;
    
    struct Seg
    {
    	int l,r;
    	int id;
    	double k,b;
    };
    int countt=0;
    
    #define leftson cur<<1
    #define rightson cur<<1|1
    #define mid ((l+r)>>1)
    Seg ans[MAXK<<2];
    int rn=(int)(4e4);
    
    inline double cal(Seg i,int x) { return i.k*x+i.b; }
    
    Seg query(int cur,int l,int r,int qs)
    {
    	if (l==r) return ans[cur];
    	Seg answ;
    	if (qs<=mid)
    		answ=query(leftson,l,mid,qs);
    	else
    		answ=query(rightson,mid+1,r,qs);
    	return cal(answ,qs)-cal(ans[cur],qs)>EPS?answ:ans[cur]; 
    }
    void change(int cur,int l,int r,Seg k)
    {
    	if (k.l<=l&&r<=k.r)//线段完全覆盖才更新 
    	{
    		if (!ans[cur].id) { ans[cur]=k; return; }
    		if (cal(k,mid)-cal(ans[cur],mid)>EPS) swap(ans[cur],k);//优势判断 
    		if (cal(k,l)-cal(ans[cur],l)>EPS) change(leftson,l,mid,k);//优势线段右侧完全优先,用劣势更新左子树 
    		if (cal(k,r)-cal(ans[cur],r)>EPS) change(rightson,mid+1,r,k);//优势线段左侧完全优先,用劣势更新右子树 
    		return;
    	}
    	if (k.l<=mid) change(leftson,l,mid,k);
    	if (k.r>mid) change(rightson,mid+1,r,k);
    }
    int main()
    {
    	scanf("%d",&OPT);
    	int lastans=0,opt;
    	int K,xx0,yy0,xx1,yy1;
    	while (OPT--)
    	{
    		scanf("%d",&opt);
    		if (!opt)
    		{
    			scanf("%d",&K);
    			K=(K+lastans-1)%39989+1;
    			printf("%d
    ",lastans=query(1,1,rn,K).id);
    		}
    		else
    		{
    			scanf("%d%d%d%d",&xx0,&yy0,&xx1,&yy1);
    			xx0=(xx0+lastans-1)%39989+1;
    			yy0=(yy0+lastans-1)%(int)(1e9)+1;
    			xx1=(xx1+lastans-1)%39989+1;
    			yy1=(yy1+lastans-1)%(int)(1e9)+1;
    			if (xx0>xx1) swap(xx0,xx1),swap(yy0,yy1);
    			countt++;
    			Seg k=(Seg){xx0,xx1,countt,0,0};
    			if (xx0==xx1) k.b=max(yy0,yy1);//垂直于x轴的线段 
    			else k.k=((double)(yy1-yy0))/((double)(xx1-xx0)),k.b=(double)yy0-k.k*xx0;
    			change(1,1,rn,k);
    		}
    	}
    	return 0;
    }
    
    [JSOI2008] Blue Mary 开公司
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define MAXN (int)(1e5+233)
    #define MAXT (int)(5e4+233)
    const double EPS=1e-8;
    const int rn=(int)(5e4);
    using namespace std;
    int tot=0;
    
    int n;
    struct Lin
    {
    	double k,b;
    }a,ans[MAXT<<2];
    inline double cal(Lin i,int x) { return i.k*x+i.b; }
    #define leftson cur<<1
    #define rightson cur<<1|1
    #define mid ((l+r)>>1)
    void change(int cur,int l,int r,Lin k)
    {
    	if (l==r)
    	{
    		if (cal(ans[cur],l)<cal(k,l)) ans[cur]=k;
    		return;
    	}
    	if (!ans[cur].k) { ans[cur]=k; return; }
    	if (cal(k,mid)-cal(ans[cur],mid)>EPS) swap(ans[cur],k);
    	if (cal(k,l)-cal(ans[cur],l)>EPS) change(leftson,l,mid,k);
    	if (cal(k,r)-cal(ans[cur],r)>EPS) change(rightson,mid+1,r,k);
    }
    double query(int cur,int l,int r,int qs)
    {
    	if (l==r) return cal(ans[cur],l);
    	double answ=cal(ans[cur],qs);
    	if (qs<=mid)
    		answ=max(answ,query(leftson,l,mid,qs));
    	else
    		answ=max(answ,query(rightson,mid+1,r,qs));
    	return answ; 
    }
    int main()
    {
    	scanf("%d",&n);
    	string opt;
    	int K;
    	for (int i=1;i<=n;i++)
    	{
    		cin>>opt;
    		if (opt[0]=='Q')
    		{
    			scanf("%d",&K);
    			if (tot==0) printf("0
    ");
    			else printf("%d
    ",(int)(query(1,1,rn,K)/100));
    //			else printf("%lf
    ",(query(1,1,rn,K)/*100*/));
    		}
    		else
    		{
    			tot++;
    			scanf("%lf%lf",&a.b,&a.k); a.b-=a.k;
    			change(1,1,rn,a);
    		}
    	}
    	return 0;
    }
    

    (tips:别for j to k 该opt j 还 operate i 了()

    01分数规划

    模型:给定一系列整数(a_1,a_2,...,a_n)(b_1,b_2,...,b_n),求解一组(x_i)(space (1 leq i leq n , x_i = 0 space or space 1))使得下式值最大化

    [frac{Sigma_{i=1}^{n}a_i*x_i}{Sigma_{i=1}^{n}b_i*x_i} ]

    解法:二分答案。把柿子拆一下

    [frac{sum_{i=1}^{n}a_i*x_i}{sum_{i=1}^{n}b_i*x_i}geq mid ]

    [sum_{i=1}^{n}a_i imes x_i-mid imes (sum_{i=1}^{n}b_i imes x_i) geq 0 ]

    [sum_{i=1}^{n}(a_i-mid imes b_i) imes x_i geq 0 ]

    那么就可以判断,若(a_i-mid*b_i < 0),就令(x_i=0),否则令(x_i=1)

    (tips:const double EPS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1)

    (tips:沃日,写到个SPFA判负环结果边权柿子是浮点数,int dis[]一发100 -> 30)

    set/multiset

    暂时没写总结啥的,先放个别人博客

    除此还差bitset,list,平板电视之类的没学(

    放个自己 AtCoder Beginner Contest 170 E - Smart Infants 的代码,在里面写使用注释算了(

    AtCoder Beginner Contest 170 E - Smart Infants
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <set>
    #define MAXN (int)(2e5+233)
    #define MAXQ (int)(2e5+233)
    using namespace std;
    int n,q;
    int S[MAXN],a[MAXN];
    multiset<int> st[MAXN],setans;
    
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&a[i],&S[i]);
    		st[S[i]].insert(a[i]);
    	}
    	for (int i=1;i<=(int)(2e5);i++)
    		if (!st[i].empty()) setans.insert(*(--st[i].end()));//--st[i].end(),返回set st[i]末尾位置迭代器。set.end()是末尾+1。加星号是取了值(
    	int c,d;
    	while (q--)
    	{
    		scanf("%d%d",&c,&d);
    		setans.erase(setans.find( *(--st[S[c]].end()) ));//find(x),找到元素x所在位置的迭代器。在multiset中,multiset.erase(x)会导致将所有值为x的元素删除,而erase(it)就删除迭代器it所在的元素。
    		if (!st[d].empty()) setans.erase(setans.find(*(--st[d].end())));
    		st[S[c]].erase(st[S[c]].find(a[c]));
    		st[d].insert(a[c]);//set.insert(x),插入一个元素x。
    		if (!st[S[c]].empty()) setans.insert(*(--st[S[c]].end()));
    		setans.insert(*(--st[d].end()));
    		printf("%d
    ",*setans.begin());
    		S[c]=d;
    	}
    	return 0;
    }
    

    草,一个很奇怪的点()记得上考场前把校章摘下来QAQ————————————————————————————————————————————————————————————————————————————————————

    By ❤千柒/Kan_kiz
  • 相关阅读:
    算法提高 12-1三角形
    poj3723_Conscription
    算法提高 8-1因式分解
    算法提高 道路和航路
    算法训练 安慰奶牛
    最短路问题(Bellman/Dijkstra/Floyd)
    最小生成树 prime+heap
    算法训练 最短路
    算法训练 最大的算式
    poj3255 Roadblocks
  • 原文地址:https://www.cnblogs.com/Kan-kiz/p/15272515.html
Copyright © 2011-2022 走看看