题解之前
中秋只有一天假!
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
",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
",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
",cnt);
for(int i=2;i<=egnum;i+=2) if(isAns[i])
printf("%d ",i/2) ;
printf("
") ;
return 0 ;
}