题解之前
中秋只有一天假!
build
以为并查集水题,发现有历史版本,于是可持久化并查集一波(爆0了)
其实有简单点的做法:用二元组记录每一次变化的siz和fa,二分查询即可。
我还在肝可持久并查集,所以就没有代码了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int MAXN=100005 ; int n, m, fa[MAXN], times[MAXN], h[MAXN], nowT, size[MAXN]; struct Info{ int tm, size ; Info(int _tm=0,int _size=0){ tm=_tm , size=_size; } }; vector<Info> vec[MAXN] ; vector<Info>::iterator it ; bool cmp(const Info &a, const Info &b){ return a.tm < b.tm ; } int findFa(int a){ if(fa[a]==a) return a; return findFa(fa[a]) ; } void mergeNodes(int u,int v){ int fu=findFa(u), fv=findFa(v) ; if(fu==fv) return ; if(h[fu]<h[fv]) swap(fu,fv) ; fa[fv]=fu, size[fu]+=size[fv], times[fv]=nowT ; if(h[fu]==h[fv]) h[fu]++ ; vec[fu].push_back(Info(nowT,size[fu])) ; } int query(int t, int a){ while(a!=fa[a] && times[a]<=t) a=fa[a] ; it=upper_bound(vec[a].begin(),vec[a].end(),Info(t,0),cmp) ; --it ; return it->size ; } int main(){ freopen("build.in","r",stdin) ; freopen("build.out","w",stdout) ; scanf("%d%d",&n,&m) ; for(int i=1;i<=n;++i) fa[i]=i, vec[i].push_back(Info(0,1)), h[i]=1, size[i]=1 ; int lastans=0 ; for(int i=1;i<=m;++i){ int t, u, v ; nowT=i ; scanf("%d%d%d",&t,&u,&v) ; u+=lastans , v+=lastans ; if(t==1){ mergeNodes(u,v) ; } else if(t==2){ printf("%d\n",lastans=query(u,v)) ; } } return 0 ; }
清华爷的代码就是不一样。
补:
做出来了。
其实二维线段树?
#include<bits/stdc++.h> #define FN "build" const int maxn=1e5+5; inline int read() { int x;char ch;bool flag=false;while(!isdigit(ch=getchar())) (ch=='-') && (flag=true); for(x=ch-'0';isdigit(ch=getchar());x=(x<<3)+(x<<1)+ch-'0'); return (flag?-x:x); } namespace HJT { int n; struct Node { Node *ls,*rs; int siz,fa; }pool[maxn*32], *root[maxn], *zero, *tail=pool; Node *newnode() { Node *nd=++tail; nd->ls=nd->rs=zero; nd->siz=nd->fa=0; return nd; } Node *Fmodify(Node *p,int l,int r,int pos,int rt) { Node *nd=newnode(); if(l==r) { nd->fa=rt; nd->siz=p->siz; return nd; } int mid=l+r>>1; if(pos<=mid) { nd->rs=p->rs; nd->ls=Fmodify(p->ls,l,mid,pos,rt); } else { nd->ls=p->ls; nd->rs=Fmodify(p->rs,mid+1,r,pos,rt); } return nd; } Node *Smodify(Node *p,int l,int r,int pos,int rt) { Node *nd=newnode(); if(l==r) { nd->fa=p->fa; nd->siz=p->siz+rt; return nd; } int mid=l+r>>1; if(pos<=mid) { nd->rs=p->rs; nd->ls=Smodify(p->ls,l,mid,pos,rt); } else { nd->ls=p->ls; nd->rs=Smodify(p->rs,mid+1,r,pos,rt); } return nd; } int Fquery(Node *nd,int l,int r,int pos) { if(l==r) return nd->fa; int mid=l+r>>1; if(pos<=mid) return Fquery(nd->ls,l,mid,pos); else return Fquery(nd->rs,mid+1,r,pos); } int Squery(Node *nd,int l,int r,int pos) { if(l==r) return nd->siz; int mid=l+r>>1; if(pos<=mid) return Squery(nd->ls,l,mid,pos); else return Squery(nd->rs,mid+1,r,pos); } int find(int i,Node *nd,int x) { int ff=Fquery(nd,1,n,x); if(ff==x) return x; return find(i,nd,ff); } Node *build(int l,int r) { Node *nd=newnode(); if(l==r) { nd->siz=1; nd->fa=l; return nd; } int mid=l+r>>1; nd->ls=build(l,mid); nd->rs=build(mid+1,r); return nd; } void work() { int m=read();n=read(); int lastans=0,tot=0; zero=++tail; zero->ls=zero->rs=zero; zero->fa=zero->siz=0; root[0]=build(1,n); for(int i=1;i<=m;i++) { int opt=read(); int u=lastans+read(); int v=lastans+read(); if(opt==1) { u=find(tot,root[tot],u); v=find(tot,root[tot],v); if(u==v) { ++tot; root[tot]=root[tot-1]; continue; } int siz1=Squery(root[tot],1,n,v); int siz2=Squery(root[tot],1,n,u); if(siz1>siz2) { std::swap(siz1,siz2); std::swap(u,v); } ++tot; root[tot]=Fmodify(root[tot-1],1,n,v,u); root[tot]=Smodify(root[tot],1,n,u,siz1); } else { int ff=find(u,root[u],v); int siz1=Squery(root[u],1,n,ff); root[tot+1]=root[tot]; ++tot; lastans=siz1; printf("%d\n",siz1); } } } } int main(){ freopen(FN".in","r",stdin); freopen(FN".out","w",stdout); HJT::work(); return 0; }
妙啊。
distribute
我真的不认识这个单词。
但是这道题水的惊人。
dp[i] 表示先手在第 i 个物品时的最大收益。
因为此题有后效性,选择从后往前for。
如果上一手换了,就是sum[i+1]-dp[i+1]+a[i](sum为前缀和);
如果没有换,就是dp[i+1]。
取max即可。
ye!
其实可以从dp[i][0/1][0/1]慢慢来,总之只要知道必须从后往前,基本就对了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define FN "distribute" const int maxn=1e5+5; int a[maxn]; long long dp,sum; int main() { freopen(FN".in","r",stdin); freopen(FN".out","w",stdout); int n;scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",a+i); dp=sum=a[n]; for(int i=n-1;i>0;i--) { sum+=a[i]; dp=std::max(dp,sum-dp); } printf("%lld",dp); return 0; }
find
讲得好!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=100005 , MAXM=MAXN<<1 ; int n , m , first[MAXN] , nexts[MAXM] , to[MAXM] , egnum=1 ; int TTT, low[MAXN], dfn[MAXN], top, kNum, tag[MAXN], q[MAXM] , qnum , stk[MAXM] ; bool isAns[MAXM], vis[MAXM] ; void addEdge(int a,int b){ nexts[++egnum]=first[a] , first[a]=egnum , to[egnum]=b ; } void tarjan(int a,int fEdge){ dfn[a]=low[a]=++TTT ; for(int i=first[a];i;i=nexts[i]){ int b=to[i] ; if((i^1)==fEdge) continue ; if(!vis[i]) vis[i]=vis[i^1]=true , stk[++top]=i ; if(!dfn[b]){ tarjan(b,i) ; low[a]=min(low[a],low[b]) ; if(low[b]>=dfn[a]){ ++kNum , qnum=0 ; int cnt=0, t; do{ t=stk[top--] ; if(tag[to[t]]!=kNum) tag[to[t]]=kNum , ++cnt ; if(tag[to[t^1]]!=kNum) tag[to[t^1]]=kNum , ++cnt ; q[++qnum]=t ; }while(t!=i); if(qnum==cnt){ for(int i=1;i<=qnum;++i){ isAns[q[i]]=isAns[q[i]^1]=true ; } } } } else if(dfn[b]<low[a]){ low[a]=min(low[a],dfn[b]) ; } } } int main(){ freopen("find.in","r",stdin) ; freopen("find.out","w",stdout) ; scanf("%d%d",&n,&m) ; for(int i=1;i<=m;++i){ int a,b; scanf("%d%d",&a,&b) ; addEdge(a,b) , addEdge(b,a) ; } for(int i=1;i<=n;++i){ if(!dfn[i]){ tarjan(i,0) ; } } int cnt=0 ; for(int i=2;i<=egnum;i+=2) if(isAns[i]) ++cnt ; printf("%d\n",cnt); for(int i=2;i<=egnum;i+=2) if(isAns[i]) printf("%d ",i/2) ; printf("\n") ; return 0 ; }
end
来源:https://www.cnblogs.com/LoLiK/p/9690309.html