zoukankan      html  css  js  c++  java
  • 51nod一日游

    忽然意识到昨天交了十几发的CE是因为没选择语言qwq
    本来准备按着题目分类刷一遍的,看来是刷不完了

    1618树或非树

    树链剖分

    思路还是挺简单的吧?
    把环拎出来单独处理(n条边的联通图存在且仅存在一个环),那么若不考虑环
    图中的连通图个数=总点数-"开"的边数
    若这条环联通了,那么实际上有一条边并没有是减少联通块个数,但答案却已经减去了,所以ans++
    边的开合用树链剖分维护即可

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    inline char nc(){
    	static char buf[100000],*p1=buf,*p2=buf;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline void read(int &x){
    	char c=nc();x=0;
    	for(;!isdigit(c);c=nc());
    	for(;isdigit(c);c=nc())x=(x<<1)+(x<<3)+c-'0';
    }
    const int N=300002;
    bool vis[N];
    int n,m,head[N],nex[N<<1],to[N<<1],cnt,q[N],tot,c[N],c1,inc[N],bl[N];
    struct data{int s,tag;}t[N<<3];
    inline void addedge(int u,int v){
    	nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v;
    }
    inline void getcyc(int x,int fa){
    	vis[x]=1,q[++tot]=x;
    	for(int i=head[x];i;i=nex[i])
    		if(!vis[to[i]])getcyc(to[i],x);
    		else if(to[i]!=fa){
    			if(c1)return;
    			for(int y=0;y!=to[i];)y=q[tot--],c[++c1]=y,inc[y]=c1;
    		}tot--;
    }
    int siz[N],dep[N],fa[N],son[N],top[N],pos[N],sz;
    inline void dfs1(int x){
    	siz[x]=1,dep[x]=dep[fa[x]]+1;
    	for(int i=head[x];i;i=nex[i]){
    		if(to[i]==fa[x]||inc[to[i]])continue;
    		fa[to[i]]=x,bl[to[i]]=bl[x],dfs1(to[i]),siz[x]+=siz[to[i]],son[x]=siz[to[i]]>siz[son[x]]?to[i]:son[x];
    	}
    }
    inline void dfs2(int x,int topf){
    	pos[x]=++sz,top[x]=topf;
    	if(!son[x])return;
    	dfs2(son[x],topf);
    	for(int i=head[x];i;i=nex[i])if(to[i]!=fa[x]&&!inc[to[i]]&&to[i]!=son[x])dfs2(to[i],to[i]);
    }
    inline void pushdown(int x,int l,int r){
    	if(l==r||!t[x].tag)return;
    	t[x].tag=0;int mid=l+r>>1;
    	t[x<<1].tag^=1,t[x<<1|1].tag^=1;
    	t[x<<1].s=mid-l+1-t[x<<1].s,t[x<<1|1].s=r-mid-t[x<<1|1].s;
    }
    inline void updata(int x,int l,int r,int L,int R){
    	if(L>R)return;
    	if(l==L&&r==R){
    		t[x].tag^=1,t[x].s=r-l+1-t[x].s;return;
    	}
    	pushdown(x,l,r);int mid=l+r>>1;
    	updata(x<<1,l,mid,L,min(R,mid)),updata(x<<1|1,mid+1,r,max(L,mid+1),R);
    	t[x].s=t[x<<1].s+t[x<<1|1].s;
    }
    inline int query(int x,int l,int r,int L,int R){
    	if(L>R)return 0;
    	if(l==L&&r==R)return t[x].s;
    	pushdown(x,l,r);int mid=l+r>>1;
    	return query(x<<1,l,mid,L,min(R,mid))+query(x<<1|1,mid+1,r,max(L,mid+1),R);
    }
    inline void upd(int x,int y){
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		updata(1,1,n+c1,pos[top[x]],pos[x]),x=fa[top[x]];
    	}
    	if(dep[x]<dep[y])swap(x,y);
    	updata(1,1,n+c1,pos[y]+1,pos[x]);
    }
    int main(){
    	read(n),read(m);int x,y,px,py;
    	for(int u,v,i=1;i<=n;i++)read(u),read(v),addedge(u,v),addedge(v,u);
    	getcyc(1,0);
    	for(int i=1;i<=c1;i++)bl[c[i]]=c[i],dfs1(c[i]),dfs2(c[i],c[i]);
    	while(m--){
    		read(x),read(y);
    		if(bl[x]==bl[y])upd(x,y);
    		else{
    			upd(x,bl[x]),upd(y,bl[y]),x=bl[x],y=bl[y],px=inc[x],py=inc[y];
    			if(px<py){
    				if(py-px<c1-py+px||py-px==c1-py+px&&c[px+1]<c[px==1?c1:px-1])updata(1,1,n+c1,px+1+n,py+n);
    				else updata(1,1,n+c1,1+n,px+n),updata(1,1,n+c1,py+1+n,c1+n);
    			}else{
    				if(px-py<c1-px+py||px-py==c1-px+py&&c[px-1]<c[px%c1+1])updata(1,1,n+c1,py+1+n,px+n);
    				else updata(1,1,n+c1,1+n,py+n),updata(1,1,n+c1,px+1+n,c1+n);
    			}
    		}
    		int ans=n-t[1].s;
    		if(query(1,1,n+c1,n+1,n+c1)==c1)ans++;
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    1742开心的小Q

    莫比乌斯

    果然数学差得已经不成样了,稍微复习了下
    做不来——
    留坑

    1636教育改革

    DP

    比较愉快了

    1789跑得比谁都快

    斜率优化

    很容易发现单调性,由此得到斜率优化;
    在树上的话是多条链同时进行的,刚开始想用可持久化数组,可是不是特别好
    想想看,记个前驱,后缀就能维护head,tail了,且一个数只会最多进队一次,O(n)的时间复杂度
    然后——
    1e18会爆炸啊,不想写高精度
    那就改代码了...rp++

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    struct data{int pre,now,sub;}Z[N];
    int nex[N],to[N],head[N],cnt;
    int num,L,R,MX;
    ll fa,n,p,C[N],ans,f[N],co[N];
    void read(ll &x){
    	char c=getchar();x=0;
    	for(;!isdigit(c);c=getchar());
    	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    }
    inline ll calc(int x,int i){return f[x]+C[i-x]+co[x];}
    void ins(int u,int v){nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v;}
    int divi(int L,int R){
        int l=L+1,r=n;
        while(l<=r){
            int mid=l+r>>1;
            if(f[L]+C[mid-L]+co[L]>=f[R]+C[mid-R]+co[R])r=mid-1;else l=mid+1;
        }
        return r;
    }
    void dfs(int x){
        int l=L,r=R;
        for(;Z[L].sub&&calc(Z[L].now,x)>=calc(Z[Z[L].sub].now,x);L=Z[L].sub);
        f[x]=(x-Z[L].now<MX)?calc(Z[L].now,x):1e18;
        ans=(!head[x]&&ans>f[x])?f[x]:ans;
        for(;Z[R].pre&&divi(Z[Z[R].pre].now,Z[R].now)>=divi(Z[R].now,x);R=Z[R].pre);
        int last=Z[R].sub,nowR=R;
        Z[++num]=(data){R,x,0},Z[R].sub=num,R=num;
        for(int i=head[x];i;i=nex[i])dfs(to[i]);
        Z[nowR].sub=last,L=l,R=r;
    }
    int main(){
        read(n),read(p);
    	for(int i=1;i<=n;i++){
            C[i]=1,MX=i;
            for(int j=1;j<=p;j++){
                C[i]=1ll*C[i]*i;
                if(C[i]>1e18)break;
            }
            if(C[i]>1e18)break;
        }
        MX=(MX==n&&C[n]<=1e18)?n+1:MX;
        ans=1e18;
        for(int i=1;i<=n;i++)read(co[i]),read(fa),ins(fa,i);
        L=R=num=1;Z[1]=(data){0,1,0};
        for(int i=head[1];i;i=nex[i])dfs(to[i]);
        return printf("%lld
    ",ans),0;
    }
    

    1623完美消除

    数位DP

    维护一个单调队列(0-9)来维护消除次数

    #include<cstdio>
    #include<cstring>
    typedef long long ll;
    ll L,R,f[20][1<<10][20];
    int k,a[20],t[11];
    bool vis[20][1<<10][20];
    inline int cal(int p,int i){return p=p&(t[i+1]-1);}
    inline ll dfs(int pos,int s,int tt,bool lim){
    	if(pos<0)return tt==k;
    	if(!lim&&f[pos][s][tt]!=-1)return f[pos][s][tt];
    	int mx=lim?a[pos]:9;ll res=0;
    	for(int i=0;i<=mx;i++){
    		if(s&t[i]||i==0)res+=dfs(pos-1,cal(s,i),tt,lim&&i==a[pos]);
    		else res+=dfs(pos-1,cal(s|t[i],i),tt+1,lim&&i==a[pos]);
    	}
    	if(!lim)f[pos][s][tt]=res;
    	return res;
    }
    inline ll solve(ll x){
    	memset(f,-1,sizeof f);
    	int pos=0;for(;x;a[pos++]=x%10,x/=10);
    	return dfs(pos-1,0,0,1);
    }
    int main(){
    	t[0]=1;for(int i=1;i<=10;i++)t[i]=t[i-1]<<1;
    	scanf("%lld%lld%d",&L,&R,&k);
    	return printf("%lld
    ",solve(R)-solve(L-1)),0;
    }
    

    1486大大走格子

    容斥,组合数

    以前最年长的学长讲过
    是个好题
    用了组合数&容斥
    如果每次只能向下或向右走
    那么从(x1,y1)走到(x2,y2) [x1<=x2,y1<=y2]的方案数是C(x2-x1+y2-y1,x2-x1)
    总方案数=C(h+w-2,h-1)-经过不能经过的位置的方案数
    关键是怎样计算经过不能经过的位置的方案
    现将点按x,y坐标排序
    f[i]表示从左上角走过来经过的第一个不能经过位置是第i个点的方案数
    则f[i]=C(a[i].x+a[i].y-2,a[i].x-1)-sigma(f[j]*C(a[i].x+a[i].y-a[j].x-a[j].y,a[i].x-a[j].x))[j<i&&a[j].x<=a[i].x&&a[j].y<=a[i].y]

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=2002,M=100002,mo=1e9+7;
    inline char nc(){
        static char buf[100000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline void read(int &x){
        char c=nc();x=0;
        for(;!isdigit(c);c=nc());
        for(;isdigit(c);c=nc())x=(x<<1)+(x<<3)+c-'0';
    }
    struct data{
        int x,y;
        bool operator<(const data &b)const{return x==b.x?y<b.y:x<b.x;}
    }a[N];
    int w,h,n;
    ll ans,c[M<<1],f[N],inv[M<<1],cinv[M<<1];
    ll C(int x,int y){return 1ll*c[x]*cinv[y]%mo*cinv[x-y]%mo;}
    int main(){
    	read(h),read(w),read(n);
    	c[0]=inv[0]=c[1]=inv[1]=cinv[0]=cinv[1]=1;for(int i=2;i<=w+h;++i)c[i]=1ll*c[i-1]*i%mo,inv[i]=1ll*(mo-(mo/i))*inv[mo%i]%mo,cinv[i]=1ll*cinv[i-1]*inv[i]%mo;
        for(int i=1;i<=n;++i)read(a[i].x),read(a[i].y);
        sort(a+1,a+1+n); 
        for(int i=1;i<=n;++i){
            f[i]=C(a[i].x+a[i].y-2,a[i].x-1);
            for(int j=1;j<i;j++)if(a[i].y>=a[j].y)f[i]=(f[i]-f[j]*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%mo)%mo;
            ans=(ans+f[i]*C(h-a[i].x+w-a[i].y,h-a[i].x)%mo)%mo;
        }
        printf("%lld
    ",((C(h+w-2,h-1)-ans)%mo+mo)%mo);
        return 0;
    }
    

    1553周期串查询

    hash和线段树

    如果[l,r]周期为d
    则[l,r-d]与[l+d,r]的hash值相同

    1672区间交

    先按照左端点排序,再用个树状数组,二分查找最右端的被覆盖至少k 次的点

    1766树上的最远点对

    线段树

    一点都不会求求直径,也算填坑

    我们知道,树上最远的距离是树的直径。
    然后,直径可以由两个点集中的直径的总共四个端点两两配对得到。
    于是我们就可以用线段树来维护这个东西。
    注意求距离要用欧拉序列,不能用倍增,否则会爆炸性超时。
    -alan_cty


    简单证明一下。
    我们看作是 x 所在的连通块通过边(x, y)连向 y 所在的连通块。
    若新直径不经过(x, y),则就是原来的两条直径取 max。
    若新直径经过(x, y),就要考虑 x 延伸到哪儿、y 延伸到哪儿了。由直径的定义可知,x 能走到的最远点之一是 x 所在连通块直径的端点,y 同理。因此这时新直径的两个端点都是旧直径的端点。
    (这个证明也适用于增量法求树的直径,即我给一棵树加一个新点,那么新直径必有一端点是旧直径的端点)
    (注意只能是树,普通图没有这些性质)
    -rzO_KQP_Orz

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100002;
    struct note{int a,b;}tr[N*5];
    int n,m,k,tot,d[N],dfn[N<<1],fir[N],sum[N],f[N<<1][18];
    int to[N<<1],nex[N<<1],w[N<<1],head[N],mi[18],cnt;
    inline char nc(){
        static char buf[100000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline void read(int &x){
        char c=nc();x=0;
        for(;!isdigit(c);c=nc());
        for(;isdigit(c);c=nc())x=(x<<1)+(x<<3)+c-'0';
    }
    void add(int u,int v,int wi){
        nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v,w[cnt]=wi;
    }
    void dfs(int x,int y){
        d[x]=d[y]+1,dfn[++tot]=x,fir[x]=tot;
        for(int i=head[x];i;i=nex[i])if(to[i]!=y)sum[to[i]]=sum[x]+w[i],dfs(to[i],x),dfn[++tot]=x;
    }
    int lca(int x,int y){
        x=fir[x],y=fir[y];
        if(x>y)swap(x,y);
        int z=log2(y-x+1);
        if(d[dfn[f[x][z]]]<d[dfn[f[y-mi[z]+1][z]]])return dfn[f[x][z]];else return dfn[f[y-mi[z]+1][z]];
    }
    inline int len(int x,int y){
        int z=lca(x,y);return sum[x]+sum[y]-2*sum[z];
    }
    note merge(note y,note z,int bz){
        note x;int mx=0,l;
        if(!bz){
            l=len(y.a,y.b);if(l>mx)mx=l,x.a=y.a,x.b=y.b;
            l=len(z.a,z.b);if(l>mx)mx=l,x.a=z.a,x.b=z.b;
        }
        l=len(y.a,z.a);if(l>mx)mx=l,x.a=y.a,x.b=z.a;
        l=len(y.a,z.b);if(l>mx)mx=l,x.a=y.a,x.b=z.b;
        l=len(y.b,z.a);if(l>mx)mx=l,x.a=y.b,x.b=z.a;
        l=len(y.b,z.b);if(l>mx)mx=l,x.a=y.b,x.b=z.b;
        return x;
    }
    void build(int v,int l,int r){
        if(l==r){tr[v].a=tr[v].b=l;return;}
        int mid=(l+r)/2;
        build(v<<1,l,mid),build(v<<1|1,mid+1,r);
        tr[v]=merge(tr[v<<1],tr[v<<1|1],0);
    }
    note find(int v,int l,int r,int x,int y){
        if(l==x&&r==y)return tr[v];
        int mid=l+r>>1;
        if(y<=mid)return find(v<<1,l,mid,x,y);
        else if(x>mid)return find(v<<1|1,mid+1,r,x,y);
        else return merge(find(v<<1,l,mid,x,mid),find(v<<1|1,mid+1,r,mid+1,y),0);
    }
    int main(){
    	freopen("t.in","r",stdin);
        read(n);
        for(int u,v,wi,i=1;i<n;i++)read(u),read(v),read(wi),add(u,v,wi),add(v,u,wi);
    	dfs(1,0),mi[0]=1;for(int i=1;i<18;i++)mi[i]=mi[i-1]<<1;
    	for(int i=1;i<=tot;i++)f[i][0]=i;
        for(int j=1;j<=log2(tot);j++)
            for(int i=1;i<=tot-mi[j]+1;i++)
                if(d[dfn[f[i][j-1]]]<d[dfn[f[i+mi[j-1]][j-1]]])f[i][j]=f[i][j-1];else f[i][j]=f[i+mi[j-1]][j-1];
        build(1,1,n);
        int x,y,z,k;
        for(read(m);m;m--){
            read(x),read(y),read(z),read(k);
            note tmp=merge(find(1,1,n,x,y),find(1,1,n,z,k),1);
            printf("%d
    ",len(tmp.a,tmp.b));
        }
        return 0;
    }
    
  • 相关阅读:
    格律詩
    React获取视频时长
    ant 入门级详解
    OpenShift证书批准及查询证书过期时间 wang
    kubeadm快速部署kubernetes集群(v1.22.3) wang
    OpenShift中SDN核心知识点总结 wang
    kubeadm快速部署kubernetes集群(v1.22.3)(二) wang
    Prometheus Operator使用ServiceMonitor自定义监控 wang
    Prometheus Operator配置k8s服务自动发现 wang
    Ceph RBD Mirroring wang
  • 原文地址:https://www.cnblogs.com/MikuKnight/p/9929807.html
Copyright © 2011-2022 走看看