**浅谈差分【复习】**

https://www.luogu.org/problem/P5026

分析:

区间加上一个等差序列,两次差分完成

code :

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
int aa[2100000],bb[2100000];
#define isdigit(x) ((x)>='0'&&(x)<='9')
inline int read(){
    int a=0,flag=1;
    char c=getchar();
    while(!isdigit(c)){
        if(c=='-')flag=-1;
        c=getchar();
    }
    while(isdigit(c)){
        a=(a<<1)+(a<<3)+c-48;
        c=getchar();
    }
    return a*flag;
}
int main(){
    n=read(),m=read();
    int *a=aa+1000000,*b=bb+1000000;
    for(int i=1;i<=n;++i){
        int v=read(),x=read();
        a[x-3*v+1]++;
        a[x-2*v+1]-=2;
        a[x+1]+=2;
        a[x+2*v+1]-=2;
        a[x+3*v+1]++;
    }
    for(int i=-40000;i<=m+40000;++i)a[i]+=a[i-1],b[i]+=b[i-1]+a[i];
    for(int i=1;i<=m;++i)printf("%d ",b[i]);
    return 0;
}

https://www.luogu.org/problem/P4623

分析:

对x轴和y轴分别维护一个树状数组,

支持区间加和单点查询,用差分

code by wzxbeliever:

#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1e5+5;
const int maxx=1e6+5;
int n,m,a,b,c,d,e,f,val;
int X[maxx],Y[maxx];
char ch1,ch2;
il void add(int x,int p){while(x<=maxx)X[x]+=p,x+=lowbit(x);}
il int query(int x){int res=0;while(x)res+=X[x],x-=lowbit(x);return res;}
il void add2(int x,int p){while(x<=maxx)Y[x]+=p,x+=lowbit(x);}
il int query2(int x){int res=0;while(x)res+=Y[x],x-=lowbit(x);return res;}
int main(){
	scanf("%d",&n);
	for(ri i=1;i<=n;i++){
	int Lmin,Rmin,Lmax,Rmax;
	Lmax=Rmax=0;Lmin=Rmin=maxx; 
	scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f);
	a++,b++,c++,d++,e++,f++;
	Lmin=min(a,min(c,e));
	Rmin=min(b,min(d,f));
	Lmax=max(a,max(c,e));
	Rmax=max(b,max(d,f));
	Lmin++;Rmin++;
	add(Lmin,1);add(Lmax,-1);
	add2(Rmin,1);add2(Rmax,-1);	
	}
	scanf("%d",&m);
	while(m--){
		cin>>ch1>>ch2>>val;val++;
		if(ch1=='x')printf("%d\n",query(val));
		else printf("%d\n",query2(val));
	}
	return 0;
}

https://www.luogu.org/problem/P2680

分析:

很早就做过了,现在发现又不会了

模仿一下考试过程:

一棵树,m个计划,可将一条边权附为0 ,最大值最小

典型的二分答案:

关键在于check()函数怎么写

考虑此时小于mid的路径都不会产生影响了

大于mid的所有边中,如果存在一条公共边,它的边权为k

使得减掉k之后所有边都小于等于mid

则return true 否则 return false

剩下就是统计出这条公共边了

用树上差分统计

总的来说就是

LCA+树上差分+二分

code by std:

#include<bits/stdc++.h>
#define IL inline
#define RI register int
#define N  300008
using namespace std;
IL void in(int &x)
{
	int f=1;x=0;char s=getchar();
	while(s>'9' or s<'0'){if(s=='-')f=-1;s=getchar();}
	while(s>='0' and s<='9'){x=x*10+s-'0';s=getchar();}
	x*=f;
}
struct node{
	int x , y , lca , dis ; 
	bool operator < (const node & a ) const {
		return dis < a.dis ; 
	}
}query[N];
int depth[N],f[N][21],Dis[N],n,head[N],tot,k,ans,init[N],cnt[N];
struct cod{int u,v,w;}edge[N<<1];
int l,r,m;
IL void add(int x,int y,int z)
{
	edge[++tot].u=head[x];
	edge[tot].v=y;
	edge[tot].w=z;
	head[x]=tot;
}
void dfs(int u,int fa , int dis)
{
	depth[u]=depth[fa]+1;
	f[u][0]=fa ; init[u] = dis;
 	for(RI i=1;(1<<i)<=depth[u];i++)
		f[u][i]=f[f[u][i-1]][i-1];
	for(RI i=head[u];i;i=edge[i].u)
	{
		if(edge[i].v==fa)continue;
		Dis[edge[i].v]=Dis[u]+edge[i].w;
		dfs(edge[i].v,u,edge[i].w);
	}
}
IL int lca(int x,int y)
{
	if(depth[x]>depth[y])swap(x,y);
	for(RI i=20;i>=0;i--)
		if(depth[y]-(1<<i)>=depth[x])
			y=f[y][i];
	if(x==y)return x;
	for(RI i=20;i>=0;i--)
	{
		if(f[x][i]==f[y][i])continue;
		x=f[x][i],y=f[y][i];
	}
	return f[x][0];
}
void dfss(int u,int fa)
{
	for(RI i=head[u];i;i=edge[i].u)
	{
		if(edge[i].v==fa)continue;
		dfss(edge[i].v,u);
		cnt[u]+=cnt[edge[i].v];
	}
}
IL bool ok(int x)
{
	int num=0,now=0;
	for(RI i=1;i<=n;i++)cnt[i]=0;
	for(RI i=1;i<=m;i++)
	{
		if(query[i].dis<=x)continue;
		cnt[query[i].x]++;cnt[query[i].y]++;
		cnt[query[i].lca]-=2;
		num++;
	}
	dfss(1,0);
	for(RI i=1;i<=n;i++)
	{
		if(cnt[i]==num)now=max(now,init[i]);
	}
	return query[m].dis-now<=x;
}
int main()
{
	in(n),in(m);
	for(RI i=1,x,y,z;i<n;i++)
	{
		in(x),in(y),in(z);
		add(x,y,z);add(y,x,z);
	}
	dfs(1,0,0);	
	for(RI i=1,x,y;i<=m;i++)
	{
		in(x),in(y);
		query[i].lca=lca(x,y);
		r=max(r,(query[i].dis=Dis[x]+Dis[y]-2*Dis[query[i].lca]));
		query[i].x = x,query[i].y = y;
	}
	sort(query+1,query+m+1);
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(ok(mid))r=mid-1;
		else l=mid+1;
	}
	printf("%d",l);
}

盗取衡水的一道题:居然和jklover以前出的题一样

题目描述

科学家在“无限神机”(Infinity MachineInfinity Machine)找到一个奇怪的机制,这个机制有NN个元件,有MM条电线连接这些元件,所有元件都是连通的。两个元件之间可能有多条电线连接。
科学家对这些元件可以任意地设置为“高电压”和“低电压”两种模式,如果一条电线的一端为高电压,另一端为低电压,这条电线就会产生电流。
为了安全的研究“无限神机”,科学家需要找到一条电线,将它的两端设为相同的电压,并且除选择的这条电线外,其它所有电线都有电流(否则就没有研究的价值了)。
有多少条电线满足这样的条件?

分析:

像这种只有两个对立面的,一般和二分图,奇环,偶环有关

首先偶环上的边肯定没法删,也可以说是不用删

再考虑奇环,首先一个奇环一定是要删一条边的,

但是如果有多个奇环,但是你又只能删一条边怎么办?

那这条边就必须被所有奇环都覆盖,并且不能再偶环上

边覆盖问题差分维护 边差分

还要注意的是这里差分没有用到LCA

因为此时lca(a,b)==a||b

还有就是这个代码里的in数组很是巧妙

code:

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to;bool dead;}e[400002];
int head[100001],cnt=1;
int n,m;
int odd[100001],eve[100001],cut[400002],depth[100001],in[100001];
bool vis[100001];
int ans;
void add(int x,int y)
{
    e[++cnt].nxt=head[x];
    e[cnt].to=y;
    head[x]=cnt;
}
void dfs(int x)
{
    vis[x]=1;
    for(int i=head[x];i;i=e[i].nxt)
    {
        if(cut[i])continue;
        cut[i]=cut[i^1]=1;
        if(vis[e[i].to])
        {
            if((depth[x]-depth[e[i].to])&1){eve[e[i].to]--;eve[x]++;}
            else{odd[e[i].to]--;odd[x]++;odd[0]++;}
        }
        else
        {
            depth[e[i].to]=depth[x]+1;
            in[e[i].to]=i;
            dfs(e[i].to);
        }
    }
}
void dfs(int x,int f)
{
    for(int i=head[x];i;i=e[i].nxt)
        if(in[e[i].to]==i)
        {
            dfs(e[i].to,x);
            odd[x]+=odd[e[i].to];
            eve[x]+=eve[e[i].to];
        }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
    if(odd[0]==1)ans=1;
    for(int i=1;i<=n;i++)if(!in[i])dfs(i,0);
    for(int i=1;i<=n;i++)
        if(in[i]&&odd[i]==odd[0]&&!eve[i])ans++;
    printf("%d",ans);
    return 0;
}

分析:

考虑维护差分数组

对于区间加[L,R],发现区间里的数的贡献不会变动,只有L-1,L+1,R+1,R

这四个位置的数的贡献可能会变

所以特判一下,利用树状数组维护前缀和

code by std:

#include <cstdio>
#define maxn 200005
#define lowbit(x) (x&(-(x)))
#define LL long long
LL a[maxn];
int n,m;
struct TreeArr{
    int a[maxn];
    void add(int x,int v){
        while(x<=n){
            a[x]+=v;
            x+=lowbit(x);
        }
    }
    int query(int pos){
        int ret=0;
        while(pos){
            ret+=a[pos];
            pos^=lowbit(pos);
        }
        return ret;
    }
} tree;
bool check(int x){
    return (a[x]<0&&a[x+1]>0)||(a[x]>0&&a[x+1]<0);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=n;i>=1;i--){
        a[i]-=a[i-1];
        if(a[i]*a[i+1]<0)tree.add(i,1);
    }
    for(int i=1;i<=m;i++){
        int op;scanf("%d",&op);
        if(op){
            int x,y,z;scanf("%d%d%d",&x,&y,&z);
            int px=check(x),py=check(y);
            int px1=check(x-1),py1=check(y+1);
            a[x]+=z;a[y+1]-=z;
            int nx=check(x),ny=check(y);
            int nx1=check(x-1),ny1=check(y+1);
            tree.add(x,nx-px);
            if(x>1)tree.add(x-1,nx1-px1);
            if(x!=y)tree.add(y,ny-py);
            tree.add(y+1,ny1-py1);
        }
        else {
            int x,y;scanf("%d%d",&x,&y);
            if(x==y)printf("0\n");
            else printf("%d\n",tree.query(y-1)-tree.query(x));
        }
    }
}
posted @ 2019-11-13 21:15  wzx_believer  阅读(167)  评论(0编辑  收藏  举报