高一下二月上旬日记
2.1
闲话
- 颓。
做题纪要
luogu P7840 「C.E.L.U-03」重构
-
按位拆平方贡献,优先队列辅助查询最小值。
-
由相关理论可知一定存在一种构造方案。
点击查看代码
struct node { ll du,val; bool operator < (const node &another) const { return val*(2*du+1)>another.val*(2*another.du+1); } }tmp; priority_queue<node>q; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,x,ans=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>x; q.push((node){1,x}); } for(i=1;i<=n-2;i++) { tmp=q.top(); q.pop(); tmp.du++; q.push(tmp); } while(q.empty()==0) { ans+=q.top().val*q.top().du*q.top().du; q.pop(); } cout<<ans<<endl; return 0; }
[ABC266Ex] Snuke Panic (2D)
-
转移的限制条件为
。 -
将第三个条件的绝对值拆开后在满足限制条件一和三后一定满足条件二,
分治优化即可。点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; struct node { ll t,x,y,val,b,c,id; }q[100010]; ll f[100010],d[100010]; bool cmp1(node a,node b) { if(a.b!=b.b) return a.b>b.b; if(a.c!=b.c) return a.c>b.c; return a.y<b.y; } bool cmp2(node a,node b) { return a.c>b.c; } bool cmp3(node a,node b) { return a.id<b.id; } struct BIT { ll c[100010]; ll lowbit(ll x) { return (x&(-x)); } void clear() { memset(c,-0x3f,sizeof(f)); } void add(ll n,ll x,ll val) { for(ll i=x;i<=n;i+=lowbit(i)) c[i]=max(c[i],val); } void del(ll n,ll x) { for(ll i=x;i<=n;i+=lowbit(i)) c[i]=-inf; } ll getsum(ll x) { ll ans=-inf; for(ll i=x;i>=1;i-=lowbit(i)) ans=max(ans,c[i]); return ans; } }T; void cdq(ll l,ll r) { if(l>=r) return; ll mid=(l+r)/2,x=0,y=0; cdq(l,mid); sort(q+l,q+mid+1,cmp2); sort(q+mid+1,q+r+1,cmp2); for(x=l,y=mid+1;y<=r;y++) { for(;q[x].c>=q[y].c&&x<=mid;x++) T.add(d[0],q[x].y,f[q[x].id]); f[q[y].id]=max(f[q[y].id],T.getsum(q[y].y)+q[y].val); } x--; for(ll i=l;i<=x;i++) T.del(d[0],q[i].y); sort(q+mid+1,q+r+1,cmp3); cdq(mid+1,r); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m=0,ans=0,i; cin>>n; for(i=1;i<=n;i++) { m++; cin>>q[m].t>>q[m].x>>q[m].y>>q[m].val; d[m]=q[m].y; q[m].b=-q[m].x+q[m].y-q[m].t; q[m].c=q[m].x+q[m].y-q[m].t; if(q[m].c>0) m--; } sort(d+1,d+m+1); d[0]=unique(d+1,d+m+1)-(d+1); sort(q+1,q+m+1,cmp1); for(i=1;i<=m;i++) { q[i].y=lower_bound(d+1,d+1+d[0],q[i].y)-d; q[i].id=i; f[i]=q[i].val; } T.clear(); cdq(1,m); for(i=1;i<=m;i++) ans=max(ans,f[i]); cout<<ans<<endl; return 0; }
2.2
闲话
- 颓。
在群里通知集训不让穿校服,貌似通知得太晚了。 让下午 前进校。在 左右到机房时机房里只有 在下象棋和 @5k_sync_closer 在敲代码,等我 收拾完东西到机房后发现人已经基本来得差不多了。- 晚上临下课时
时这个紧张的寒假我们已经过完了,不知道我们在家有没有尽兴地放烟花,反正他除夕晚上拉着一车烟花准备到小区门口放的时候就看见有两个穿着警服的人在巡逻,他瞬间就怂了,只有村里管得没有那么严。以后这么紧张的寒假估计就没有了,我们要好好珍惜这次机会。距离省选也就剩不到一个月的时间了,要营造一个紧张的氛围,不要再和普通学生比我们怎么怎么样了,高二的现在已经到了全力冲刺的阶段了,高一的也要和高二一样,要是再像之前有人反映的有些高一的状态太差、题也不好好讲、整天吊儿郎当,那他就只能把我们踢回去学 了。下次放假预定是在 中午之前,让我们先给家长打电话说一下。 - 坐电梯临走时
说明天早上 到位,就不要再犯困了。
做题纪要
CF914E Palindromes in a Tree
-
考虑点分治。因字符集很小,状压维护方案数。
-
此时是一个形如根链加的形式,树上差分即可。
-
实现时可以最后再处理经过分治中心
的答案,注意点对的去重。点击查看代码
struct node { ll nxt,to; }e[400010]; ll head[200010],ans[200010],cnt=0; char s[200010]; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct Divide_On_Tree { ll siz[200010],weight[200010],vis[200010],d[200010],cnt[(1<<20)+10],dis[200010],tmp[(1<<20)+10],center; queue<ll>q; void init(ll n) { center=0; get_center(1,0,n); get_siz(center,0); divide(center); } void get_center(ll x,ll fa,ll n) { siz[x]=1; weight[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) center=x; } void get_siz(ll x,ll fa) { siz[x]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void get_dis(ll x,ll fa) { d[x]=0; dis[x]=dis[fa]^(1<<(s[x]-'a')); cnt[dis[x]]++; q.push(dis[x]); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) get_dis(e[i].to,x); } } ll work(ll x,ll rt) { ll sum=0,c; for(ll i=0;i<=20;i++) { if(i==20) c=dis[x]^dis[rt]; else c=dis[x]^dis[rt]^(1<<i); if(c==dis[rt]) { ans[rt]++; d[x]++; } sum+=cnt[c]-tmp[c]; d[x]+=cnt[c]-tmp[c]; } return sum; } ll dfs1(ll x,ll fa,ll rt) { ll sum=work(x,rt); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) sum+=dfs1(e[i].to,x,rt); } return sum; } void dfs2(ll x,ll fa,ll rt) { for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dfs2(e[i].to,x,rt); d[x]+=d[e[i].to]; } } if(x!=rt) ans[x]+=d[x]; } void clear(ll x,ll fa,ll op) { tmp[dis[x]]+=op; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) clear(e[i].to,x,op); } } void divide(ll x) { ll sum=0; vis[x]=1; d[x]=0; dis[x]=1<<(s[x]-'a'); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) get_dis(e[i].to,x); } for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { clear(e[i].to,x,1); sum+=dfs1(e[i].to,x,x); clear(e[i].to,x,-1); } } ans[x]+=sum/2; dfs2(x,0,x); while(q.empty()==0) { cnt[q.front()]=0; q.pop(); } for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,x); divide(center); } } } }D; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,u,v,i; cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } scanf("%s",s+1); D.init(n); for(i=1;i<=n;i++) cout<<ans[i]+1<<" "; return 0; }
QOJ 8672. 排队
CF1172F Nauuo and Bug
2.3
闲话
说今天的模拟赛 @aakennes 学长会根据得分情况颁发小奖品。- 上午
打 @aakennes 学长组的欢乐赛模拟赛。 - 下午讲题。
说今天的题目是不是有点简单了,得分情况还算比较不错。 说要让自己保持一个紧张的状态,要是紧张不起来就去看看倒计时。知识点和相关 要赶紧补,不要说因为太多补不完就直接摆烂了;放假前提到的题目分享要提上日程;他还安排了一个值日表,让我们按照表上的安排值日。- 临吃晚饭前
说 @ppllxx_9G 特地从杭州给我们带来了两只烧鸡,因早上吃饭时间有点短,遂延迟到了晚饭的时候让我们在食堂分着吃了,不要在食堂大肆炫耀、宣扬。撕开包装发现是烤鸭,量有点小,肉很“紧致”。
做题纪要
P891. 打游戏
P888. 字符串会上树
P889. 挖宝石
P890. 电梯系统
P892. 魔法水晶
P895. 赌博
P897. 对战
P893. 随机数序列
luogu P10473 表达式计算4
-
多余括号的处理很魔性,辅助代码进行理解,需要特殊处理形如
)1+1(
的情况。点击查看代码
string s; stack<int>s1; stack<char>s2; int val(char x) { if(x=='(') return 0; if(x=='+'||x=='-') return 1; if(x=='*'||x=='/') return 2; if(x=='^') return 3; return -1; } void work() { int a=s1.top(); s1.pop(); int b=s1.top(); s1.pop(); char c=s2.top(); s2.pop(); if(c=='+') s1.push(b+a); if(c=='-') s1.push(b-a); if(c=='*') s1.push(b*a); if(c=='/') s1.push(b/a); if(c=='^') s1.push(pow(b,a)); } int ask(string s) { int tmp=0,flag=0,cnt=0; for(int i=0;i<s.size();i++) cnt+=(s[i]=='(')-(s[i]==')'); if(cnt>=0) for(int i=1;i<=cnt;i++) s=s+')'; else for(int i=1;i<=-cnt;i++) s='('+s; s='('+s+')'; for(int i=1;i<s.size();i++) { if(s[i]=='-'&&s[i-1]=='(') s.insert(i,"0"); } for(int i=0;i<s.size();i++) { if('0'<=s[i]&&s[i]<='9') { tmp=tmp*10+s[i]-'0'; flag=1; if(i==s.size()-1) s1.push(tmp); } else { if(flag==1) { s1.push(tmp); tmp=flag=0; } if(s[i]=='(') s2.push(s[i]); else if(s[i]==')') { while(s2.top()!='(') work(); s2.pop(); } else { while(s2.empty()==0&&val(s2.top())>=val(s[i])) work(); s2.push(s[i]); } } if(i==s.size()-1) while(s2.empty()==0) work(); } return s1.top(); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif cin>>s; cout<<ask(s)<<endl; return 0; }
P901. 线段树改造2
P882. 波斯菊
luogu P3642 [APIO2016] 烟火表演
2.4
闲话
- 下午找
问有没有体育课,他说他去找其他教练问问,但长时间没有得到回复。过了一会儿 过来了说现在是敏感时期,就不给我们补体活了,穿着校服在校园里疯跑被举报了我们就完蛋了,等到开学后再正常上体育课和体活,现在让我们先坚持坚持。 - 吃晚饭前众人尝试坐电梯,但进去后被
从门口截住了,但他还是让我们坐电梯下去了。 - 吃完晚饭后我在看 luogu P3295 [SCOI2016] 萌萌哒 课件时
突然神秘兮兮地过来问我现在刷题感觉困难吗,模拟赛能拿到分吗,话说他是不会看排行榜吗。
做题纪要
P900. 超级计算机
luogu P10604 BZOJ4317 Atm 的树
-
二分答案后需要查询从
出发距离 的路径条数,点分树上vector
二分维护。点击查看代码
struct node { int nxt,to,w; }e[30010]; int head[15010],cnt=0; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } struct LCA { int siz[15010],fa[15010],dep[15010],dis[15010],son[15010],top[15010]; void init() { dfs1(1,0); dfs2(1,1); } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dis[e[i].to]=dis[x]+e[i].w; dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) u=fa[top[u]]; else v=fa[top[v]]; } return dep[u]<dep[v]?u:v; } int get_dis(int x,int y) { return dis[x]+dis[y]-2*dis[lca(x,y)]; } }L; struct Divide_On_Tree { int siz[15010],weight[15010],vis[15010],fa[15010],center; vector<int>v[2][15010]; void init(int n) { center=0; get_center(1,0,n); get_siz(center,0); build(center); for(int i=1;i<=n;i++) { for(int rt=i;rt!=0;rt=fa[rt]) { v[0][rt].push_back(L.get_dis(rt,i)); if(fa[rt]!=0) v[1][rt].push_back(L.get_dis(fa[rt],i)); } } for(int i=1;i<=n;i++) { sort(v[0][i].begin(),v[0][i].end()); sort(v[1][i].begin(),v[1][i].end()); } } void get_center(int x,int fa,int n) { siz[x]=1; weight[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) center=x; } void get_siz(int x,int fa) { siz[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void build(int x) { vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,x); fa[center]=x; build(center); } } } int query(int x,int mid) { int ans=upper_bound(v[0][x].begin(),v[0][x].end(),mid)-v[0][x].begin()-1; for(int rt=x;fa[rt]!=0;rt=fa[rt]) { ans+=upper_bound(v[0][fa[rt]].begin(),v[0][fa[rt]].end(),mid-L.get_dis(fa[rt],x))-v[0][fa[rt]].begin(); ans-=upper_bound(v[1][rt].begin(),v[1][rt].end(),mid-L.get_dis(fa[rt],x))-v[1][rt].begin(); } return ans; } }D; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,k,u,v,w,l,r,ans,mid,i; cin>>n>>k; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } L.init(); D.init(n); for(i=1;i<=n;i++) { l=0; r=80000000; ans=0; while(l<=r) { mid=(l+r)/2; if(D.query(i,mid)>=k) { ans=mid; r=mid-1; } else { l=mid+1; } } cout<<ans<<endl; } return 0; }
luogu P9233 [蓝桥杯 2023 省 A] 颜色平衡树
-
树上启发式合并,对桶开桶判断是否合法。
点击查看代码
int c[200010],cnt[200010],num[200010],ans=0; vector<int>e[200010]; void add(int u,int v) { e[u].push_back(v); } struct DSU_On_Tree { int siz[200010],fa[200010],son[200010],dfn[200010],out[200010],pos[200010],tot=0; void add_rt(int x) { num[cnt[c[x]]]--; cnt[c[x]]++; num[cnt[c[x]]]++; } void del_rt(int x) { num[cnt[c[x]]]--; cnt[c[x]]--; num[cnt[c[x]]]++; } void add_tree(int x) { for(int i=dfn[x];i<=out[x];i++) add_rt(pos[i]); } void del_tree(int x) { for(int i=dfn[x];i<=out[x];i++) del_rt(pos[i]); } void dfs1(int x) { tot++; dfn[x]=tot; pos[tot]=x; siz[x]=1; for(int i=0;i<e[x].size();i++) { dfs1(e[x][i]); siz[x]+=siz[e[x][i]]; son[x]=(siz[e[x][i]]>siz[son[x]])?e[x][i]:son[x]; } out[x]=tot; } void dfs2(int x,int flag) { for(int i=0;i<e[x].size();i++) { if(e[x][i]!=son[x]) dfs2(e[x][i],0); } if(son[x]!=0) dfs2(son[x],1); for(int i=0;i<e[x].size();i++) { if(e[x][i]!=son[x]) add_tree(e[x][i]); } add_rt(x); ans+=(cnt[c[x]]*num[cnt[c[x]]]==siz[x]); if(flag==0) del_tree(x); } }D; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,i; cin>>n; for(i=1;i<=n;i++) { cin>>c[i]>>D.fa[i]; if(i!=1) add(D.fa[i],i); } D.dfs1(1); D.dfs2(1,1); cout<<ans<<endl; return 0; }
luogu P3285 [SCOI2014] 方伯伯的OJ
-
暴力开点空间开不下,借鉴珂朵莉树的思路将没有被操作过的点缩成一段,接着平衡树维护编号与排名的双射即可。 -
查排名时需要不断往上跳,累加左子树贡献,需要额外记录父亲节点。
点击查看代码
map<int,int>f; map<int,int>::iterator query(int pos) { map<int,int>::iterator it=f.lower_bound(pos); if(it!=f.end()&&it->first==pos) return it; it--; return it; } struct BST { int root,rt_sum=0; struct FHQ_Treap { int son[2],fa,l,r,val,rnd,cnt,siz; }tree[500010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define fa(rt) (tree[rt].fa) BST() { rt_sum=0; srand(time(0)); } int build_rt(int l,int r) { rt_sum++; f[l]=rt_sum;//map 存储 [l,r] 这段区间所在平衡树上的位置 lson(rt_sum)=rson(rt_sum)=fa(rt_sum)=0; tree[rt_sum].l=l; tree[rt_sum].r=r; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=r-l+1; return rt_sum; } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; fa(rt)=0; if(lson(rt)!=0) fa(lson(rt))=rt; if(rson(rt)!=0) fa(rson(rt))=rt; } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } void split(int rt,int k,int &x,int &y) { if(rt==0) { x=y=0; return; } if(tree[lson(rt)].siz+tree[rt].cnt<=k) { x=rt; split(rson(rt),k-tree[lson(rt)].siz-tree[rt].cnt,rson(x),y); } else { y=rt; split(lson(rt),k,x,lson(y)); } pushup(rt); } void insert(int pos,int l,int r) { int x,y; split(root,pos-1,x,y); root=merge(x,merge(build_rt(l,r),y)); } void del(int l,int r) { int x,y,z; split(root,r,y,z); split(y,l-1,x,y); root=merge(x,z); } int kth_min(int rt,int k) { if(k<=tree[lson(rt)].siz) return kth_min(lson(rt),k); if(tree[lson(rt)].siz+tree[rt].cnt<k) return kth_min(rson(rt),k-tree[lson(rt)].siz-tree[rt].cnt); return tree[rt].l+k-tree[lson(rt)].siz-1; } int query_rk(int rt) { int ans=tree[lson(rt)].siz+tree[rt].cnt; for(;fa(rt)!=0;rt=fa(rt)) ans+=(rson(fa(rt))==rt)*(tree[lson(fa(rt))].siz+tree[fa(rt)].cnt); return ans; } int update(int x,int id,int op) { map<int,int>::iterator it=query(x); int l=tree[it->second].l,r=tree[it->second].r,rk=query_rk(it->second),ans=rk-r+x; del(rk-r+l,rk); if(l<=x-1) insert(rk-r+l,l,x-1);//将 [x,x] 从 [l,r] 中踢出并分裂得到 [l,x-1] 和 [x+1,r] if(x+1<=r) insert(ans,x+1,r); if(op==1) insert(ans,id,id); else insert(id,x,x); return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,pd,x,y,ans=0,i; cin>>n>>m; T.insert(1,1,n); for(i=1;i<=m;i++) { cin>>pd>>x; x-=ans; if(pd==1) { cin>>y; y-=ans; cout<<(ans=T.update(x,y,1))<<endl; } if(pd==2) cout<<(ans=T.update(x,1,2))<<endl; if(pd==3) cout<<(ans=T.update(x,n,3))<<endl; if(pd==4) cout<<(ans=T.kth_min(T.root,x))<<endl; } return 0; }
luogu P3960 [NOIP 2017 提高组] 列队
-
对于每一行分别用一棵平衡树维护,仍像上题一样需要用到的时候再分裂区间。
-
特殊处理第
列。点击查看代码
struct BST { ll root[300010],rt_sum=0; struct FHQ_Treap { ll son[2],l,r,rnd,cnt,siz; }tree[1200010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) BST() { rt_sum=0; srand(time(0)); } ll build_rt(ll l,ll r) { rt_sum++; lson(rt_sum)=rson(rt_sum)=0; tree[rt_sum].l=l; tree[rt_sum].r=r; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=r-l+1; return rt_sum; } void pushup(ll rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } void split_rt(ll rt,ll k) { if(k>=tree[rt].cnt) return; rson(rt)=merge(build_rt(tree[rt].l+k,tree[rt].r),rson(rt)); tree[rt].r=tree[rt].l+k-1; tree[rt].cnt=k; pushup(rt); } void split(ll rt,ll k,ll &x,ll &y) { if(rt==0) { x=y=0; return; } if(tree[lson(rt)].siz<k) { split_rt(rt,k-tree[lson(rt)].siz); x=rt; split(rson(rt),k-tree[lson(rt)].siz-tree[rt].cnt,rson(x),y); } else { y=rt; split(lson(rt),k,x,lson(y)); } pushup(rt); } ll update(ll nx,ll ny,ll m) { ll ans,x,y,z,_x,_y,_z; if(ny==m) { split(root[0],nx,x,y); split(x,nx-1,x,z); ans=tree[z].l; root[0]=merge(merge(x,y),z); } else { split(root[nx],ny,x,y); split(x,ny-1,x,z); ans=tree[z].l; split(root[0],nx,_x,_y); split(_x,nx-1,_x,_z); root[nx]=merge(merge(x,y),_z); root[0]=merge(merge(_x,_y),z); } return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,q,x,y,i; cin>>n>>m>>q; for(i=1;i<=n;i++) { T.root[i]=T.build_rt((i-1)*m+1,i*m-1); T.root[0]=T.merge(T.root[0],T.build_rt(i*m,i*m)); } for(i=1;i<=q;i++) { cin>>x>>y; cout<<T.update(x,y,m)<<endl; } return 0; }
luogu P5350 序列
-
可持久化平衡树支持区间复制,需要在
merge
时也新建节点。 -
复制时不能连带随机值一起复制,否则会复制树的形态,详见 FHQ Treap 的做法的复杂度问题 ,需要在合并时根据子树大小随机合并。
-
需要定期重构。
点击查看代码
const int p=1000000007; int a[300010],top=0; int qadd(int a,int b) { return a+b>=p?a+b-p:a+b; } struct PDS_BST { int root,rt_sum; struct FHQ_Treap { int son[2],val,sum,siz,col,add,rev; }tree[40000010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) PDS_BST() { rt_sum=0; srand(time(0)); } int build_rt(int val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].add=tree[rt_sum].rev=0; tree[rt_sum].val=tree[rt_sum].sum=val; tree[rt_sum].siz=1; tree[rt_sum].col=-1; return rt_sum; } int copy_rt(int rt) { rt_sum++; tree[rt_sum]=tree[rt]; return rt_sum; } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+1; tree[rt].sum=qadd(qadd(tree[lson(rt)].sum,tree[rson(rt)].sum),tree[rt].val); } void pushlazy(int &rt,int col,int add,int rev) { if((col==-1&&add==0&&rev==0)||rt==0) return; rt=copy_rt(rt); if(col!=-1) { tree[rt].add=0; tree[rt].val=tree[rt].col=col; tree[rt].sum=1ll*tree[rt].siz*col%p; } if(add!=0) { tree[rt].add=qadd(tree[rt].add,add); tree[rt].val=qadd(tree[rt].val,add); tree[rt].sum=qadd(tree[rt].sum,1ll*tree[rt].siz*add%p); } if(rev!=0) { swap(lson(rt),rson(rt)); tree[rt].rev^=1; } } void pushdown(int rt) { pushlazy(lson(rt),tree[rt].col,tree[rt].add,tree[rt].rev); pushlazy(rson(rt),tree[rt].col,tree[rt].add,tree[rt].rev); tree[rt].col=-1; tree[rt].add=tree[rt].rev=0; } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; int rt; pushdown(rt1); pushdown(rt2); if(rand()%(tree[rt1].siz+tree[rt2].siz)<tree[rt1].siz) { rt=copy_rt(rt1); rson(rt)=merge(rson(rt),rt2); } else { rt=copy_rt(rt2); lson(rt)=merge(rt1,lson(rt)); } pushup(rt); return rt; } void split(int rt,int k,int &x,int &y) { if(rt==0) { x=y=0; return; } pushdown(rt); if(tree[lson(rt)].siz+1<=k) { x=copy_rt(rt); split(rson(rt),k-tree[lson(rt)].siz-1,rson(x),y); pushup(x); } else { y=copy_rt(rt); split(lson(rt),k,x,lson(y)); pushup(y); } } int query(int l,int r) { int ans,x,y,z; split(root,r,x,y); split(x,l-1,x,z); ans=tree[z].sum; root=merge(merge(x,z),y); return ans; } void update(int l,int r,int col,int add,int rev) { int x,y,z; split(root,r,x,y); split(x,l-1,x,z); pushlazy(z,col,add,rev); root=merge(merge(x,z),y); } void paste(int l1,int r1,int l2,int r2) { int x,y,z,_y,_z; split(root,r2,x,y); split(x,l2-1,x,z); split(root,r1,_y,_z); split(_y,l1-1,_y,_z); root=merge(merge(x,_z),y); } void change(int l1,int r1,int l2,int r2) { int x,y,z,_y,_z; if(r1>r2) { swap(l1,l2); swap(r1,r2); } split(root,r2,x,y); split(x,l2-1,x,z); split(x,r1,x,_y); split(x,l1-1,x,_z); root=merge(merge(merge(merge(x,z),_y),_z),y); } void dfs(int rt) { if(rt==0) return; pushdown(rt); dfs(lson(rt)); a[++top]=tree[rt].val; dfs(rson(rt)); } void build(int &rt,int l,int r) { if(l>r) { rt=0; return; } int mid=(l+r)/2; rt=build_rt(a[mid]); build(lson(rt),l,mid-1); build(rson(rt),mid+1,r); pushup(rt); } void rebuild(int n) { if(rt_sum>3600000) { top=0; dfs(root); rt_sum=0;build(root,1,n); } } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,pd,l1,r1,l2,r2,x,i; cin>>n>>m; for(i=1;i<=n;i++) cin>>a[i]; T.build(T.root,1,n); for(i=1;i<=m;i++) { cin>>pd>>l1>>r1; if(pd==1) cout<<T.query(l1,r1)<<endl; if(pd==2||pd==3) { cin>>x; T.update(l1,r1,(pd==2)?x:-1,(pd==3)*x,0); } if(pd==4) { cin>>l2>>r2; T.paste(l1,r1,l2,r2); } if(pd==5) { cin>>l2>>r2; T.change(l1,r1,l2,r2); } if(pd==6) T.update(l1,r1,-1,0,1); T.rebuild(n); } top=0; T.dfs(T.root); for(i=1;i<=n;i++) cout<<a[i]<<" "; return 0; }
luogu P11622 [Ynoi Easy Round 2025] TEST_176
-
将值域分成
两部分,前者更改为 ,后者不变。 -
对于相同节点的合并,可以挂一个并查集辅助。
点击查看代码
struct node { int pos,id; ll x; }ql[200010],qr[200010]; int it[200010]; ll a[200010],ans[200010]; bool cmp(node a,node b) { return a.pos<b.pos; } struct BST { int id[200010],root,rt_sum; struct FHQ_Treap { int son[2],fa,rnd,cnt,siz,mul; ll val,minn,maxx,add; }tree[200010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define fa(rt) (tree[rt].fa) BST() { rt_sum=0; srand(time(0)); } int find(int x) { return id[x]==x?x:id[x]=find(id[x]); } int build_rt(ll val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=fa(rt_sum)=tree[rt_sum].add=tree[rt_sum].mul=0; tree[rt_sum].val=tree[rt_sum].minn=tree[rt_sum].maxx=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=1; return id[rt_sum]=rt_sum; } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; fa(rt)=0; if(lson(rt)!=0) { tree[rt].minn=tree[lson(rt)].minn; fa(lson(rt))=rt; } else tree[rt].minn=tree[rt].val; if(rson(rt)!=0) { tree[rt].maxx=tree[rson(rt)].maxx; fa(rson(rt))=rt; } else tree[rt].maxx=tree[rt].val; } void pushlazy(int rt,ll add,int mul) { if(rt==0) return; if(mul==1) { tree[rt].mul^=1; tree[rt].add=-tree[rt].add+add; tree[rt].val=-tree[rt].val+add; tree[rt].minn=-tree[rt].minn+add; tree[rt].maxx=-tree[rt].maxx+add; swap(tree[rt].minn,tree[rt].maxx); } else { tree[rt].add+=add; tree[rt].val+=add; tree[rt].minn+=add; tree[rt].maxx+=add; } } void pushdown(int rt) { if(tree[rt].mul==1) swap(lson(rt),rson(rt)); pushlazy(lson(rt),tree[rt].add,tree[rt].mul); pushlazy(rson(rt),tree[rt].add,tree[rt].mul); tree[rt].add=tree[rt].mul=0; } void split(int rt,ll val,int &x,int &y) { if(rt==0) { x=y=0; return; } pushdown(rt); if(tree[rt].val<=val) { x=rt; split(rson(rt),val,rson(x),y); } else { y=rt; split(lson(rt),val,x,lson(y)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; pushdown(rt1); pushdown(rt2); if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } int join(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].minn>tree[rt2].maxx) return merge(rt2,rt1); if(tree[rt1].maxx<tree[rt2].minn) return merge(rt1,rt2); if(tree[rt1].rnd>tree[rt2].rnd) swap(rt1,rt2); pushdown(rt1); pushdown(rt2); int x,y,z; split(rt2,tree[rt1].val,x,y); split(x,tree[rt1].val-1,x,z); if(z!=0) { tree[rt1].cnt+=tree[z].siz; tree[rt1].siz+=tree[z].siz; id[z]=rt1; } lson(rt1)=join(lson(rt1),x); rson(rt1)=join(rson(rt1),y); pushup(rt1); return rt1; } int insert(ll val) { int x,y,z; split(root,val,x,y); split(x,val-1,x,z); if(z!=0) { tree[z].cnt++; tree[z].siz++; } else z=build_rt(val); root=merge(merge(x,z),y); return z; } void update(ll val) { int x,y; split(root,val>>1,x,y); pushlazy(x,val,1); root=join(x,y); } ll query(int rt) { vector<int>s; rt=find(rt); for(int x=rt;x!=0;x=fa(x)) s.push_back(x); reverse(s.begin(),s.end()); for(int i=0;i<s.size();i++) pushdown(s[i]); return tree[rt].val; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,l,r,i,j,k; ll x; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%lld",&a[i]); for(i=1;i<=m;i++) { scanf("%lld%d%d",&x,&l,&r); ql[i]=(node){l,i,x}; qr[i]=(node){r,i,x}; } sort(ql+1,ql+1+m,cmp); sort(qr+1,qr+1+m,cmp); for(i=j=k=1;i<=n;i++) { for(;ql[j].pos==i;j++) it[ql[j].id]=T.insert(ql[j].x); T.update(a[i]); for(;qr[k].pos==i;k++) ans[qr[k].id]=T.query(it[qr[k].id]); } for(i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }
-
另一种投机取巧的方法是常数巨大,可以尝试把两种合并方式拼起来后做数据点分治。
点击查看代码
struct node { int pos,id; ll x; }ql[200010],qr[200010]; int it[200010]; ll a[200010],ans[200010]; bool cmp(node a,node b) { return a.pos<b.pos; } struct BST { int root,rt_sum; struct FHQ_Treap { int son[2],fa,rnd,cnt,siz,mul; ll val,minn,maxx,add; }tree[200010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define fa(rt) (tree[rt].fa) BST() { rt_sum=0; srand(time(0)); } int build_rt(ll val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=fa(rt_sum)=tree[rt_sum].add=tree[rt_sum].mul=0; tree[rt_sum].val=tree[rt_sum].minn=tree[rt_sum].maxx=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=1; return rt_sum; } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; fa(rt)=0; if(lson(rt)!=0) { tree[rt].minn=tree[lson(rt)].minn; fa(lson(rt))=rt; } else tree[rt].minn=tree[rt].val; if(rson(rt)!=0) { tree[rt].maxx=tree[rson(rt)].maxx; fa(rson(rt))=rt; } else tree[rt].maxx=tree[rt].val; } void pushlazy(int rt,ll add,int mul) { if(rt==0) return; if(mul==1) { tree[rt].mul^=1; tree[rt].add=-tree[rt].add+add; tree[rt].val=-tree[rt].val+add; tree[rt].minn=-tree[rt].minn+add; tree[rt].maxx=-tree[rt].maxx+add; swap(tree[rt].minn,tree[rt].maxx); } else { tree[rt].add+=add; tree[rt].val+=add; tree[rt].minn+=add; tree[rt].maxx+=add; } } void pushdown(int rt) { if(tree[rt].mul==1) swap(lson(rt),rson(rt)); pushlazy(lson(rt),tree[rt].add,tree[rt].mul); pushlazy(rson(rt),tree[rt].add,tree[rt].mul); tree[rt].add=tree[rt].mul=0; } void split(int rt,ll val,int &x,int &y) { if(rt==0) { x=y=0; return; } pushdown(rt); if(tree[rt].val<=val) { x=rt; split(rson(rt),val,rson(x),y); } else { y=rt; split(lson(rt),val,x,lson(y)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; pushdown(rt1); pushdown(rt2); if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } int join1(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].minn>tree[rt2].maxx) return merge(rt2,rt1); if(tree[rt1].maxx<tree[rt2].minn) return merge(rt1,rt2); if(tree[rt1].rnd>tree[rt2].rnd) swap(rt1,rt2); pushdown(rt1); pushdown(rt2); int x,y; split(rt2,tree[rt1].val,x,y); lson(rt1)=join1(lson(rt1),x); rson(rt1)=join1(rson(rt1),y); pushup(rt1); return rt1; } int join2(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; int rt=0,x; for(;rt2!=0;swap(rt1,rt2)) { split(rt1,tree[rt2].minn,x,rt1); rt=merge(rt,x); } rt=merge(rt,rt1); return rt; } int join(int rt1,int rt2,ll val) { if(abs(val)>=3000000) return join1(rt1,rt2); else return join2(rt1,rt2); } int insert(ll val) { int x,y; split(root,val,x,y); root=merge(merge(x,build_rt(val)),y); return rt_sum; } void update(ll val) { int x,y; split(root,val>>1,x,y); pushlazy(x,val,1); root=join(x,y,val); } ll query(int rt) { vector<int>s; for(int x=rt;x!=0;x=fa(x)) s.push_back(x); reverse(s.begin(),s.end()); for(int i=0;i<s.size();i++) pushdown(s[i]); return tree[rt].val; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,l,r,i,j,k; ll x; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%lld",&a[i]); for(i=1;i<=m;i++) { scanf("%lld%d%d",&x,&l,&r); ql[i]=(node){l,i,x}; qr[i]=(node){r,i,x}; } sort(ql+1,ql+1+m,cmp); sort(qr+1,qr+1+m,cmp); for(i=j=k=1;i<=n;i++) { for(;ql[j].pos==i;j++) it[ql[j].id]=T.insert(ql[j].x); T.update(a[i]); for(;qr[k].pos==i;k++) ans[qr[k].id]=T.query(it[qr[k].id]); } for(i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }
luogu P3295 [SCOI2016] 萌萌哒
-
表优化建图板子。 -
将必须选择相同的数的位置放到一个连通块内,设最后有
个极大连通块,则 即为所求。 -
考虑优化合并过程,不妨把一段区间挂在左端点上通过长度进行映射,然后进行倍增,并查集辅助进行连边。
- 完全意义上的倍增去连边是没有必要的,观察到可以通过
表进行合并(两段需要进行连边的区间有交只会增加连边的数量,但不会影响答案取值,且不在复杂度瓶颈上)。
- 完全意义上的倍增去连边是没有必要的,观察到可以通过
-
最后下传标记至对应节点即可。
点击查看代码
const ll p=1000000007; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } struct DSU_ST { ll fa[20][100010]; void init(ll n) { for(ll j=0;j<=log2(n);j++) { for(ll i=1;i<=n;i++) fa[j][i]=i; } } ll find(ll x,ll k) { return fa[k][x]==x?x:fa[k][x]=find(fa[k][x],k); } void merge(ll x,ll y,ll k) { x=find(x,k); y=find(y,k); if(x!=y) fa[k][x]=y; } void add(ll l1,ll r1,ll l2,ll r2) { ll t=log2(r1-l1+1); merge(l1,l2,t); merge(r1-(1<<t)+1,r2-(1<<t)+1,t); } void pushdown(ll n) { for(ll j=log2(n);j>=1;j--) { for(ll i=1;i<=n;i++) { ll k=find(i,j); merge(i,k,j-1); merge(i+(1<<(j-1)),k+(1<<(j-1)),j-1); } } } }D; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,l1,r1,l2,r2,ans=0,i; cin>>n>>m; D.init(n); for(i=1;i<=m;i++) { cin>>l1>>r1>>l2>>r2; D.add(l1,r1,l2,r2); } D.pushdown(n); for(i=1;i<=n;i++) ans+=(D.fa[0][i]==i); cout<<9*qpow(10,ans-1,p)%p<<endl; return 0; }
CF1290A Mind Control
-
考虑控制前
个人,此时有 即为所求。 -
设
后单调队列维护即可。点击查看代码
int a[3510],b[3510]; deque<int>q; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int t,n,m,k,ans,i,j; cin>>t; for(j=1;j<=t;j++) { q.clear(); cin>>n>>m>>k; k=min(k,m-1); for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=m;i++) b[i]=max(a[i],a[n-m+i]); for(i=1;i<=m-k;i++) { while(q.empty()==0&&b[q.back()]>b[i]) q.pop_back(); q.push_back(i); } ans=b[q.front()]; for(i=1;i<=k;i++) { while(q.empty()==0&&q.front()<i+1) q.pop_front(); while(q.empty()==0&&b[q.back()]>b[i+m-k]) q.pop_back(); q.push_back(i+m-k); ans=max(ans,b[q.front()]); } cout<<ans<<endl; } return 0; }
luogu P5609 [Ynoi2013] 对数据结构的爱
-
考虑维护
顺次经过 后 的次数。 -
线段树上每个节点
存储顺次经过 后 的次数为 时的最小值 ,查询时直接二分即可。 -
能被 更新当且仅当 ,观察到此时 一定比 优,双指针维护即可。 -
空间复杂度为
,需要查理线段树。 -
略带卡常,加个快读即可。
点击查看代码
namespace IO{ #ifdef LOCAL FILE*Fin(fopen("test.in","r")),*Fout(fopen("test.out","w")); #else FILE*Fin(stdin),*Fout(stdout); #endif class qistream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qistream(FILE*_fp=stdin):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;}qistream&operator>>(char&x){x=getch();while(isspace(x))x=getch();return*this;}template<class T>qistream&operator>>(T&x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=x*10+buf[p]-'0';x=flag?-x:x;return*this;}char getch(){p+BLOCK>=SIZE?flush():void();return buf[p++];}qistream&operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i=0;for(;ch>' ';++i,ch=getch())str[i]=ch;str[i]='\0';return*this;}}qcin(Fin); class qostream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qostream(FILE*_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream&operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=-x,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while(x);for(int i=0,j=len-1;i<j;++i,--j)std::swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream&operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}qostream&operator<<(const char*s){for(int i=0;s[i];++i)putch(s[i]);return*this;}}qcout(Fout); } #define cin IO::qcin #define cout IO::qcout const ll inf=0x3f3f3f3f3f3f3f3f; ll a[1000010],p; struct SMT { ll sum[2000010]; vector<ll>c[2000010]; #define lson(rt) (mid<<1) #define rson(rt) (mid<<1|1) void pushup(int rt,int l,int r,int mid) { sum[rt]=sum[lson(rt)]+sum[rson(rt)]; for(int i=0,j=0;i<=mid-l+1;i++) { for(;j<=r-mid&&c[lson(rt)][i+1]-1+sum[lson(rt)]-i*p>=c[rson(rt)][j];j++) c[rt][i+j]=min(c[rt][i+j],max(c[lson(rt)][i],c[rson(rt)][j]-(sum[lson(rt)]-i*p))); j--; } } void build(int rt,int l,int r) { c[rt].resize(r-l+3); for(int i=0;i<r-l+3;i++) c[rt][i]=(i>=1)?inf:-inf; if(l==r) { sum[rt]=a[l]; c[rt][1]=p-a[l]; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt,l,r,mid); } ll query(int rt,int l,int r,int x,int y,ll val) { if(x<=l&&r<=y) { return val+sum[rt]-(upper_bound(c[rt].begin(),c[rt].end(),val)-c[rt].begin()-1)*p; } int mid=(l+r)/2; if(y<=mid) return query(lson(rt),l,mid,x,y,val); if(x>mid) return query(rson(rt),mid+1,r,x,y,val); return query(rson(rt),mid+1,r,x,y,query(lson(rt),l,mid,x,y,val)); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,l,r,x,i; ll ans=0; cin>>n>>m>>p; for(i=1;i<=n;i++) cin>>a[i]; T.build(1,1,n); for(i=1;i<=m;i++) { cin>>l>>r>>x; l^=ans; r^=ans; x^=ans; cout<<(ans=T.query(1,1,n,l,r,x))<<endl; ans=(ans%n+n)%n; } return 0; }
2.5
闲话
- 上午
打 @Chen_jr | @Delov | @APJifengc 学长组的模拟赛。 - 下午讲题,讲题时因我在学网络流
过来问我们是都不听讲题的吗。 说现在因怕引起舆情所以就先不给我们上体育课了,等开学了再说;放假前提到的每人准备两道题不知道我们准备了没有;互测的话是三人一场每人一题,我们要是有想出的题就感觉和他说,他好安排时间。
做题纪要
luogu P9335 [Ynoi2001] 雪に咲く花
-
把
vector
改成链式前向星后加个快点的快读就过了。点击查看代码
#include <algorithm> #include <bit> #include <cassert> #include <cstdint> #include <cstring> #include <limits> #include <numeric> #include <type_traits> #include <vector> #ifndef __OY_LINUXIO__ #define __OY_LINUXIO__ #include <algorithm> #include <cmath> #include <cstdint> #include <string> #include <vector> #ifdef __unix__ #include <sys/mman.h> #include <sys/stat.h> #endif #if __cpp_constexpr >= 201907L #define CONSTEXPR20 constexpr #define INLINE20 constexpr #else #define CONSTEXPR20 #define INLINE20 inline #endif #define cin OY::LinuxIO::InputHelper<>::get_instance() #define cout OY::LinuxIO::OutputHelper::get_instance() #define endl '\n' #ifndef INPUT_FILE #define INPUT_FILE "in.txt" #endif #ifndef OUTPUT_FILE #define OUTPUT_FILE "out.txt" #endif namespace OY { namespace LinuxIO { static constexpr size_t INPUT_BUFFER_SIZE = 1 << 26, OUTPUT_BUFFER_SIZE = 1 << 20; #ifdef OY_LOCAL static constexpr char input_file[] = INPUT_FILE, output_file[] = OUTPUT_FILE; #else static constexpr char input_file[] = "", output_file[] = ""; #endif template <typename U, size_t E> struct TenPow { static constexpr U value = TenPow<U, E - 1>::value * 10; }; template <typename U> struct TenPow<U, 0> { static constexpr U value = 1; }; struct InputPre { uint32_t m_data[0x10000]; CONSTEXPR20 InputPre() { std::fill(m_data, m_data + 0x10000, -1); for (size_t i = 0, val = 0; i != 10; i++) for (size_t j = 0; j != 10; j++) m_data[0x3030 + i + (j << 8)] = val++; } }; struct OutputPre { uint32_t m_data[10000]; CONSTEXPR20 OutputPre() { uint32_t *c = m_data; for (size_t i = 0; i != 10; i++) for (size_t j = 0; j != 10; j++) for (size_t k = 0; k != 10; k++) for (size_t l = 0; l != 10; l++) *c++ = i + (j << 8) + (k << 16) + (l << 24) + 0x30303030; } }; template <size_t MMAP_SIZE = 1 << 30> struct InputHelper { static INLINE20 InputPre pre{}; char *m_p, *m_c, *m_end; InputHelper(FILE *file = stdin) { #ifdef __unix__ struct stat _stat; auto fd = fileno(file); fstat(fd, &_stat); m_c = m_p = (char *)mmap(nullptr, _stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); m_end = m_p + _stat.st_size; #else uint32_t size = fread(m_c = m_p = new char[INPUT_BUFFER_SIZE], 1, INPUT_BUFFER_SIZE, file); m_end = m_p + size; #endif } static InputHelper<MMAP_SIZE> &get_instance() { static InputHelper<MMAP_SIZE> s_obj(*input_file ? fopen(input_file, "rt") : stdin); return s_obj; } template <typename Tp, typename std::enable_if<std::is_unsigned<Tp>::value & std::is_integral<Tp>::value>::type * = nullptr> InputHelper &operator>>(Tp &x) { x = 0; while (!isdigit(*m_c)) m_c++; x = *m_c++ ^ '0'; while (~pre.m_data[*reinterpret_cast<uint16_t *&>(m_c)]) x = x * 100 + pre.m_data[*reinterpret_cast<uint16_t *&>(m_c)++]; if (isdigit(*m_c)) x = x * 10 + (*m_c++ ^ '0'); return *this; } template <typename Tp, typename std::enable_if<std::is_signed<Tp>::value & std::is_integral<Tp>::value>::type * = nullptr> InputHelper &operator>>(Tp &x) { typename std::make_unsigned<Tp>::type t{}; bool sign{}; while (!isdigit(*m_c)) sign = (*m_c++ == '-'); t = *m_c++ ^ '0'; while (~pre.m_data[*reinterpret_cast<uint16_t *&>(m_c)]) t = t * 100 + pre.m_data[*reinterpret_cast<uint16_t *&>(m_c)++]; if (isdigit(*m_c)) t = t * 10 + (*m_c++ ^ '0'); x = sign ? -t : t; return *this; } InputHelper &operator>>(char &x) { while (*m_c <= ' ') m_c++; x = *m_c++; return *this; } InputHelper &operator>>(std::string &x) { while (*m_c <= ' ') m_c++; char *c = m_c; while (*c > ' ') c++; x.assign(m_c, c - m_c), m_c = c; return *this; } InputHelper &operator>>(std::string_view &x) { while (*m_c <= ' ') m_c++; char *c = m_c; while (*c > ' ') c++; x = std::string_view(m_c, c - m_c), m_c = c; return *this; } }; struct OutputHelper { static INLINE20 OutputPre pre{}; FILE *m_file; char m_p[OUTPUT_BUFFER_SIZE], *m_c, *m_end; OutputHelper(FILE *file = stdout) { m_file = file; m_c = m_p, m_end = m_p + OUTPUT_BUFFER_SIZE; } ~OutputHelper() { flush(); } static OutputHelper &get_instance() { static OutputHelper s_obj(*output_file ? fopen(output_file, "wt") : stdout); return s_obj; } void flush() { fwrite(m_p, 1, m_c - m_p, m_file), m_c = m_p; } OutputHelper &operator<<(char x) { if (m_end - m_c < 20) flush(); *m_c++ = x; return *this; } OutputHelper &operator<<(std::string_view s) { if (m_end - m_c < s.size()) flush(); memcpy(m_c, s.data(), s.size()), m_c += s.size(); return *this; } OutputHelper &operator<<(uint64_t x) { if (m_end - m_c < 20) flush(); #define CASEW(w) \ case TenPow<uint64_t, w - 1>::value... TenPow<uint64_t, w>::value - 1: \ *(uint32_t *)m_c = pre.m_data[x / TenPow<uint64_t, w - 4>::value]; \ m_c += 4, x %= TenPow<uint64_t, w - 4>::value; switch (x) { CASEW(19); CASEW(15); CASEW(11); CASEW(7); case 100 ... 999: *(uint32_t *)m_c = pre.m_data[x * 10]; m_c += 3; break; CASEW(18); CASEW(14); CASEW(10); CASEW(6); case 10 ... 99: *(uint32_t *)m_c = pre.m_data[x * 100]; m_c += 2; break; CASEW(17); CASEW(13); CASEW(9); CASEW(5); case 0 ... 9: *m_c++ = '0' + x; break; default: *(uint32_t *)m_c = pre.m_data[x / TenPow<uint64_t, 16>::value]; m_c += 4; x %= TenPow<uint64_t, 16>::value; CASEW(16); CASEW(12); CASEW(8); case 1000 ... 9999: *(uint32_t *)m_c = pre.m_data[x]; m_c += 4; break; } #undef CASEW return *this; } OutputHelper &operator<<(uint32_t x) { if (m_end - m_c < 20) flush(); #define CASEW(w) \ case TenPow<uint32_t, w - 1>::value... TenPow<uint32_t, w>::value - 1: \ *(uint32_t *)m_c = pre.m_data[x / TenPow<uint32_t, w - 4>::value]; \ m_c += 4, x %= TenPow<uint32_t, w - 4>::value; switch (x) { default: *(uint32_t *)m_c = pre.m_data[x / TenPow<uint32_t, 6>::value]; m_c += 4; x %= TenPow<uint32_t, 6>::value; CASEW(6); case 10 ... 99: *(uint32_t *)m_c = pre.m_data[x * 100]; m_c += 2; break; CASEW(9); CASEW(5); case 0 ... 9: *m_c++ = '0' + x; break; CASEW(8); case 1000 ... 9999: *(uint32_t *)m_c = pre.m_data[x]; m_c += 4; break; CASEW(7); case 100 ... 999: *(uint32_t *)m_c = pre.m_data[x * 10]; m_c += 3; break; } #undef CASEW return *this; } OutputHelper &operator<<(int64_t x) { if (x >= 0) return (*this) << uint64_t(x); else return (*this) << '-' << uint64_t(-x); } OutputHelper &operator<<(int32_t x) { if (x >= 0) return (*this) << uint32_t(x); else return (*this) << '-' << uint32_t(-x); } }; } } #endif uint head[1000010],a[1000010],b[1000010],c[1000010],hsum[1000010],t[1000010],lazy[1000010],ans[5000010],cnt=0; struct node { uint nxt,to,id; }q[5000010]; void add(uint l,uint r,uint id) { cnt++; q[cnt].nxt=head[r]; q[cnt].to=l; q[cnt].id=id; head[r]=cnt; } uint ask(uint i,uint tim) { return hsum[i]+(tim-t[i])*lazy[i]; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif uint n,m,l,r,i,j; cin>>n>>m; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=n;i++) cin>>b[i]; for(i=1;i<=n;i++) cin>>c[i]; for(i=1;i<=m;i++) { cin>>l>>r; add(l,r,i); } for(i=1;i<=n;i++) { for(j=i-1;j>=1;j--) { if((a[j]&a[j+1])==a[j]&&(b[j]|b[j+1])==b[j]&&__gcd(c[j],c[j+1])==c[j]) break; a[j]&=a[j+1]; b[j]|=b[j+1]; c[j]=__gcd(c[j],c[j+1]); } hsum[i]=hsum[i-1]+(i-1-t[i-1])*lazy[i-1]; j++; for(;j<=i;j++) { hsum[j]=hsum[j]+(i-1-t[j])*lazy[j]; lazy[j]=lazy[j-1]+a[j]*b[j]*c[j]; t[j]=i-1; } for(j=head[i];j!=0;j=q[j].nxt) ans[q[j].id]=hsum[i]+(i-t[i])*lazy[i]-(hsum[q[j].to-1]+(i-t[q[j].to-1])*lazy[q[j].to-1]); } for(i=1;i<=m;i++) cout<<ans[i]<<endl; return 0; }
luogu P3376 【模板】网络最大流
- 多倍经验: luogu P2740 [USACO4.2] 草地排水Drainage Ditches | luogu B3606 [图论与代数结构 501] 网络流_1 | luogu B3607 [图论与代数结构 502] 网络流_2 | UVA820 因特网带宽 Internet Bandwidth
- 定义
为边的容量,特别地当 时可以钦定 。 - 定义
为边的流量,其满足 。- 常将原图中的边减出一条反向边来满足流量可能为负数的情况,从而支持下文的 退流 操作。
- 定义
的净流量为 。当 时总是有 ,即除源、汇点外任何节点都不存储流。 - 剩余容量
在 时也满足 。
- 若一条从
到 的路径上各条边的剩余容量都 ,则称这条路径为一条增广路。所有节点和剩余容量 的边构成的子图称为残量网络。- 对于一条增光路,我们给每条边
都加上等量的流量,以令整个网络的流量增加,这一过程被称为增广。 - 通过退流操作将增广过程中流量增加的边的反向边的流量相应减少,但我们只关注反向边的剩余流量,表示后续走的过程中我们可能通过走反向边来和原先正向的增广操作进行抵消从而支持反悔操作。
- Edmonds-Krap 增广路算法
-
不断通过
寻找增广路(由于退流操作的存在,任意一条增广路均符合题意)直至不存在增广路,沿途计算剩余容量的最小值对 进行修改。 -
需要记录前驱节点方便从
反着走回 。 -
时间复杂度为
,一般能够处理 的网络,可能会被卡。点击查看代码
struct MaxFlow { struct node { ll nxt,to,cap,flow; }e[10010]; ll head[210],vis[210],incf[210],pre[210],cnt=1; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt;//同时存储反向边,通过 ^1 操作快速查询 } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; incf[s]=0x3f3f3f3f3f3f3f3f; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { incf[e[i].to]=min(incf[x],e[i].cap-e[i].flow); pre[e[i].to]=i; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll update(ll s,ll t)// 退流 { for(ll x=t;x!=s;x=e[pre[x]^1].to) { e[pre[x]].flow+=incf[t]; e[pre[x]^1].flow-=incf[t]; } return incf[t]; } ll Edmonds_Krap(ll s,ll t) { ll ans=0; while(bfs(s,t)==true) ans+=update(s,t); return ans; } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,w,i; cin>>n>>m>>s>>t; for(i=1;i<=m;i++) { cin>>u>>v>>w; F.add(u,v,w); } cout<<F.Edmonds_Krap(s,t)<<endl; return 0; }
-
- Dinic 算法
-
Edmonds-Krap 每轮可能会便利整个残量网络,但只找出
条增广路,考虑进一步优化。 -
通过
到 的最短路/层次建立分层图。接着在分层图上进行 寻找分层图上的增广路,在回溯的过程中进行退流操作直至不存在增广路。 -
因每个点可以流向多条出边也可以从多条入边流到,所以在
寻找增广路时需要当前弧优化。具体地,对于每个节点 维护 的出边中第一条还有必要尝试的出边,可以证明这并不影响正确性。 -
代码中加入了适量优化及剪枝。
-
时间复杂度为
,一般能够处理 的网络;在容量均为 的网络上时间复杂度为 。点击查看代码
struct MaxFlow { struct node { ll nxt,to,cap,flow; }e[10010]; ll head[210],cur[210],vis[210],dis[210],cnt=1; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; q.push(s); vis[s]=1; cur[s]=head[s]; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; q.push(e[i].to); vis[e[i].to]=1; cur[e[i].to]=head[e[i].to];// 仅复制残量网络上能到达的点 if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt)// sum=flow 的时候没有必要再寻找了 { cur[x]=i;//当前弧优化 // 特别地,最后一条可能没有被流满,需要被保留 if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) // 前面本质上是一个多路增广,当寻找到一条从 s 到 t 的增广路后没必要重新从 s 出发寻找增广路,而是直接从增广路上某个分岔点进行寻找 { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));// flow-sum 计算阻塞量寻找增广路 if(tmp==0) dis[e[i].to]=0;// 无用点优化:删去无法再进行增广的点 sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp;// 退流 } } return sum;//实际能得到的流量 } ll Dinic(ll s,ll t) { ll ans=0; while(bfs(s,t)==true) ans+=dfs(s,t,0x3f3f3f3f3f3f3f3f); return ans; } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,w,i; cin>>n>>m>>s>>t; for(i=1;i<=m;i++) { cin>>u>>v>>w; F.add(u,v,w); } cout<<F.Dinic(s,t)<<endl; return 0; }
-
- 对于一条增光路,我们给每条边
luogu P3381 【模板】最小费用最大流
- 多倍经验: UVA10594 Data Flow | luogu B3608 [图论与代数结构 601] 最小费用最大流
- 定义
为边的单位流量费用。当 的流量为 时其费用为 。- 由于退流操作的存在,仍规定
。
- 由于退流操作的存在,仍规定
- 费用流的前提是在最大流的基础上再进行考虑的。
- 求解最小费用流时仅将
寻找增广路的过程替换为 求单位费用最小的增广路后其他操作不变。-
求解最大费用流时换为求单位费用最大的增广路即可。
-
Edmonds-Krap 费用流
点击查看代码
struct MaxFlow_MinCost { const ll inf=0x3f3f3f3f3f3f3f3f; struct node { ll nxt,to,w,cap,flow; }e[100010]; ll head[5010],vis[5010],incf[5010],dis[5010],pre[5010],cnt=1,flow,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); queue<ll>q; dis[s]=0; incf[s]=inf; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; incf[e[i].to]=min(incf[x],e[i].cap-e[i].flow); pre[e[i].to]=i; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return (dis[t]!=inf); } void update(ll s,ll t) { for(ll x=t;x!=s;x=e[pre[x]^1].to) { e[pre[x]].flow+=incf[t]; e[pre[x]^1].flow-=incf[t]; } flow+=incf[t]; cost+=incf[t]*dis[t]; } void Edmonds_Krap(ll s,ll t) { flow=cost=0; while(spfa(s,t)==true) update(s,t); } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,w,_w,i; cin>>n>>m>>s>>t; for(i=1;i<=m;i++) { cin>>u>>v>>w>>_w; C.add(u,v,w,_w); } C.Edmonds_Krap(s,t); cout<<C.flow<<" "<<C.cost<<endl; return 0; }
-
ZKW 费用流
-
增广时可行的边不一定像 Dinic 那样是分层图,且不一定是
,此时需要打vis
标记并在回溯时撤销。 -
当前弧优化效果不明显。
点击查看代码
struct MaxFlow_MinCost { const ll inf=0x3f3f3f3f3f3f3f3f; struct node { ll nxt,to,w,cap,flow; }e[100010]; ll head[5010],vis[5010],cur[5010],dis[5010],pre[5010],cnt=1,flow,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); queue<ll>q; dis[s]=0; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return (dis[t]!=inf); } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; vis[x]=1; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } void Dinic(ll s,ll t) { flow=cost=0; while(spfa(s,t)==true) { for(ll x=dfs(s,t,inf);x!=0;x=dfs(s,t,inf)) flow+=x; } } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,w,_w,i; cin>>n>>m>>s>>t; for(i=1;i<=m;i++) { cin>>u>>v>>w>>_w; C.add(u,v,w,_w); } C.Dinic(s,t); cout<<C.flow<<" "<<C.cost<<endl; return 0; }
-
-
- 增广过程中存在负环的话需要特殊处理,可能需要消圈算法。
luogu P1344 [USACO4.4] 追查坏牛奶 Pollutant Control
-
最大流最小割定理:任意一个网络的最大流量等于最小割中边的容量总和。
- 显然不存在最小割小于最大流的情况。考虑如何构造最小割等于最大流。
- 构造残量网络上能到达的点和剩余点分别作为割的两个集合即可。连接分属两个集合的正向边即为所求最小割。
-
最多流和最小割仅在数值上相等,其他方面基本没有联系。
-
以损失为容量先求出最小割。然后将没有满流的正向边容量改成
,满流的正向边容量改成 得到最少割边数量。点击查看代码
struct MinCut { struct node { int nxt,to,cap,flow; }e[2010]; int head[50],cur[50],vis[50],dis[50],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); cur[x]=head[x]; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int ans=0; while(bfs(s,t)==true) ans+=dfs(s,t,0x3f3f3f3f); return ans; } void rebuild() { for(int i=2;i<=cnt;i++) { if(i%2==1) e[i].cap=0; else if(e[i].cap==e[i].flow) e[i].cap=1; else e[i].cap=0x3f3f3f3f; e[i].flow=0; } } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,u,v,w,i; cin>>n>>m; s=1; t=n; for(i=1;i<=m;i++) { cin>>u>>v>>w; C.add(u,v,w); } cout<<C.Dinic(s,t)<<" "; C.rebuild(); cout<<C.Dinic(s,t)<<endl; return 0; }
CF1572D Bridge Club
[AGC031E] Snuke the Phantom Thief
2.6
闲话
- 上午
打 accoders NOI 的模拟赛。 - 下午讲题,讲完题后
说明天写数学专题,让我们今天改完题后先预习下相关概念,基本都是《高等数学》上面的。没有体育课。 - 部分高三的零零散散地自愿进校了,
问我们吃饭用长时间排队吗。
做题纪要
luogu P1345 [USACO5.4] 奶牛的电信Telecowmunication
-
同 luogu P7984 [USACO21DEC] Tickets P ,考虑把割点转化成割边。
-
将
拆成入点和出点,其间容量为 ,剩下的边容量为 。然后从 的出点向 的入点求最小割(源、汇点不能删除)。点击查看代码
const int inf=0x3f3f3f3f; struct MinCut { struct node { int nxt,to,cap,flow; }e[6010]; int head[210],cur[210],vis[210],dis[210],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); cur[x]=head[x]; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,u,v,i; cin>>n>>m>>s>>t; s+=n; for(i=1;i<=n;i++) C.add(i,n+i,1); for(i=1;i<=m;i++) { cin>>u>>v; C.add(u+n,v,inf); C.add(v+n,u,inf); } cout<<C.Dinic(s,t)<<endl; return 0; }
SP300 CABLETV - Cable TV Network
-
枚举源汇点取
。点击查看代码
const int inf=0x3f3f3f3f; int u[6010],v[6010]; struct MinCut { struct node { int nxt,to,cap,flow; }e[6010]; int head[110],cur[110],vis[110],dis[110],cnt=1; void clear() { memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); cnt=1; } void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); cur[x]=head[x]; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { for(int i=2;i<=cnt;i++) e[i].flow=0; int flow=0; while(bfs(s,t)==true) { flow+=dfs(s,t,inf); } return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int q,n,m,s,t,ans,i,j; char c; cin>>q; for(j=1;j<=q;j++) { C.clear(); cin>>n>>m; ans=inf; for(i=1;i<=n;i++) C.add(i,n+i,1); for(i=1;i<=m;i++) { cin>>c>>u[i]>>c>>v[i]>>c; u[i]++; v[i]++; C.add(u[i]+n,v[i],inf); C.add(v[i]+n,u[i],inf); } for(s=1;s<=n;s++) { for(t=1;t<=n;t++) { if(s!=t) ans=min(ans,C.Dinic(s+n,t)); } } cout<<((n<=1||ans==inf)?n:ans)<<endl; } return 0; }
LibreOJ 115. 无源汇有上下界可行流
-
设
为 的流量下界。不妨假设原图上每条边 已经流了 的初始流,在新图(残量网络)上加入容量 的边然后进行调整。具体地,考虑构造另一个流量不平衡的附加流使得这两个流合并后流量平衡。 -
若
的初始净流量 说明入流量过大,令虚拟源点 向 连一条 的附加边;若 的初始净流量 说明流量平衡,不需要处理;若 的初始净流量 说明出流量过大,令 向虚拟汇点 连一条 的附加边。若附加边满流说明能够使得这个点流量平衡。 -
求出
到 的最大流,判断附加边是否满流即可。通过残量网络上原来的边的流量确定方案。点击查看代码
ll up[80010],down[80010]; struct UpDownFlow { struct node { ll nxt,to,cap,flow; }e[80010]; ll head[510],f[510],cur[510],dis[510],vis[510],cnt=1; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } void _add(ll u,ll v,ll up,ll down) { add(u,v,up-down); f[u]-=down; f[v]+=down; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); cur[x]=head[x]; for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } ll Dinic(ll s,ll t) { ll flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,0x3f3f3f3f3f3f3f3f); return flow; } bool check(ll n) { ll s=n+1,t=n+2,sum=0; for(ll i=1;i<=n;i++) { if(f[i]>0) { sum+=f[i]; add(s,i,f[i]); } if(f[i]<0) add(i,t,-f[i]); } return Dinic(s,t)>=sum; } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,u,v,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>u>>v>>down[i]>>up[i]; F._add(u,v,up[i],down[i]); } if(F.check(n)==true) { cout<<"YES"<<endl; for(i=1;i<=m;i++) cout<<down[i]+F.e[i*2].flow<<endl; } else cout<<"NO"<<endl; return 0; }
P903. 鸬鹚
LibreOJ 116. 有源汇有上下界最大流
-
对于源汇上下界可行流,其
的流量可以不平衡,不妨加入一条从 到 的上界为 下界为 的附加边使得其平衡。若有解则可行流量为新加入的从 到 的附加边的流量。 -
至于最大流,只需要在求出一组可行流的删去所有附加边后的残量网络上再求一遍从
到 的最大流,与可行流量相加即可。点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; ll up[80010],down[80010]; struct UpDownMaxFlow { struct node { ll nxt,to,cap,flow; }e[80010]; ll head[510],f[510],cur[510],dis[510],vis[510],cnt=1,val,limit; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } void _add(ll u,ll v,ll up,ll down) { add(u,v,up-down); f[u]-=down; f[v]+=down; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow&&i<=limit) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow&&i<=limit) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } ll Dinic(ll s,ll t) { ll flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } bool check(ll n,ll s,ll t) { ll _s=n+1,_t=n+2,sum=0,tmp; _add(t,s,inf,0); tmp=head[t]; for(ll i=1;i<=n;i++) { if(f[i]>0) { sum+=f[i]; add(_s,i,f[i]); } if(f[i]<0) add(i,_t,-f[i]); } limit=inf; sum-=Dinic(_s,_t); limit=tmp; val=e[limit].flow; e[limit].cap=e[limit].flow=e[limit^1].cap=e[limit^1].flow=0; return sum<=0; } ll query(ll s,ll t) { return val+Dinic(s,t); } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,i; cin>>n>>m>>s>>t; for(i=1;i<=m;i++) { cin>>u>>v>>down[i]>>up[i]; F._add(u,v,up[i],down[i]); } if(F.check(n,s,t)==true) cout<<F.query(s,t)<<endl; else cout<<"please go home to sleep"<<endl; return 0; }
luogu P5192 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流
-
对天数和美少女分别建点。
点击查看代码
const int inf=0x3f3f3f3f; struct UpDownMaxFlow { struct node { int nxt,to,cap,flow; }e[800010]; int head[1510],f[1510],cur[1510],dis[1510],vis[1510],cnt=1,val,limit; void clear() { memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); memset(f,0,sizeof(f)); cnt=1; } void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } void _add(int u,int v,int up,int down) { add(u,v,up-down); f[u]-=down; f[v]+=down; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow&&i<=limit) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow&&i<=limit) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } bool check(int n,int s,int t) { int _s=n+1,_t=n+2,sum=0,tmp; _add(t,s,inf,0); tmp=head[t]; for(int i=1;i<=n;i++) { if(f[i]>0) { sum+=f[i]; add(_s,i,f[i]); } if(f[i]<0) add(i,_t,-f[i]); } limit=inf; sum-=Dinic(_s,_t); limit=tmp; val=e[limit].flow; e[limit].cap=e[limit].flow=e[limit^1].cap=e[limit^1].flow=0; return sum<=0; } int query(int s,int t) { return val+Dinic(s,t); } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,c,d,x,up,down,i,j; while(cin>>n>>m) { F.clear(); s=n+m+1; t=n+m+2; for(i=1;i<=m;i++) { cin>>x; F._add(n+i,t,inf,x); } for(i=1;i<=n;i++) { cin>>c>>d; F._add(s,i,d,0); for(j=1;j<=c;j++) { cin>>x>>down>>up; x++; F._add(i,n+x,up,down); } } cout<<((F.check(n+m+2,s,t)==true)?F.query(s,t):-1)<<endl<<endl; } return 0; }
luogu P1383 高级打字机
-
主席树板子。
点击查看代码
struct PDS_SMT { int root[100010],rt_sum; struct SegmentTree { int ls,rs,siz; char c; }tree[100010<<5]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) int copy_rt(int rt) { rt_sum++; tree[rt_sum]=tree[rt]; return rt_sum; } void update(int pre,int &rt,int l,int r,char c) { rt=copy_rt(pre); tree[rt].siz++; if(l==r) { tree[rt].c=c; return; } int mid=(l+r)/2; if(tree[lson(rt)].siz==mid-l+1) update(rson(pre),rson(rt),mid+1,r,c); else update(lson(pre),lson(rt),l,mid,c); } char query(int rt,int l,int r,int k) { if(l==r) return tree[rt].c; int mid=(l+r)/2; if(k<=tree[lson(rt)].siz) return query(lson(rt),l,mid,k); else return query(rson(rt),mid+1,r,k-tree[lson(rt)].siz); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,x,cnt=0,i; char pd,c; cin>>n; for(i=1;i<=n;i++) { cin>>pd; if(pd=='T') { cin>>c; cnt++; T.update(T.root[cnt-1],T.root[cnt],1,n,c); } if(pd=='U') { cin>>x; cnt++; T.root[cnt]=T.root[cnt-x-1]; } if(pd=='Q') { cin>>x; cout<<T.query(T.root[cnt],1,n,x)<<endl; } } return 0; }
LibreOJ 117. 有源汇有上下界最小流
-
求一遍从
到 的最大流,用可行流量与其相减得到最小流。点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; ll up[500010],down[500010]; struct UpDownMinFlow { struct node { ll nxt,to,cap,flow; }e[500010]; ll head[50010],f[50010],cur[50010],dis[50010],vis[50010],cnt=1,val,limit; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } void _add(ll u,ll v,ll up,ll down) { add(u,v,up-down); f[u]-=down; f[v]+=down; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow&&i<=limit) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow&&i<=limit) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } ll Dinic(ll s,ll t) { ll flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } bool check(ll n,ll s,ll t) { ll _s=n+1,_t=n+2,sum=0,tmp; _add(t,s,inf,0); tmp=head[t]; for(ll i=1;i<=n;i++) { if(f[i]>0) { sum+=f[i]; add(_s,i,f[i]); } if(f[i]<0) add(i,_t,-f[i]); } limit=inf; sum-=Dinic(_s,_t); limit=tmp; val=e[limit].flow; e[limit].cap=e[limit].flow=e[limit^1].cap=e[limit^1].flow=0; return sum<=0; } ll query(ll s,ll t) { return val-Dinic(t,s); } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,i; cin>>n>>m>>s>>t; for(i=1;i<=m;i++) { cin>>u>>v>>down[i]>>up[i]; F._add(u,v,up[i],down[i]); } if(F.check(n,s,t)==true) cout<<F.query(s,t)<<endl; else cout<<"please go home to sleep"<<endl; return 0; }
2.7
闲话
- 早上起床后
查迟到,然后又去宿舍查内务。 - 听多校数学专题的视频。
让我们赶紧把数学没学的知识点赶紧补一下,就不要再管证明等不太重要的东西了。 - 上午听完课后
问我们有没有会做 的,他打算整个新 的 ,他用抖音 生成的图片不太理想,还需要调整下细节,问我们有没有会 的。然后 跟我们讲他大学学数学分析的时候,科任老师上课特别有激情,能看出来他是真的很喜欢数学,希望我们也能喜欢上数学,否则学习过程中会感到很痛苦。
做题纪要
luogu P2472 [SCOI2007] 蜥蜴
-
对
进行拆点。点击查看代码
const int inf=0x3f3f3f3f; struct MaxFlow { struct node { int nxt,to,cap,flow; }e[76100]; int head[810],dis[810],vis[810],cur[810],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,d,s,t,sum=0,i,j,k,h; char c; cin>>n>>m>>d; s=2*n*m+1; t=2*n*m+2; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>c; if(c!='0') { F.add((i-1)*m+j,(i-1)*m+j+n*m,c-'0'); for(k=i-d;k<=i+d;k++) { for(h=j-d;h<=j+d;h++) { if((i-k)*(i-k)+(j-h)*(j-h)<=d*d) { if(1<=k&&k<=n&&1<=h&&h<=m) F.add((i-1)*m+j+n*m,(k-1)*m+h,inf); else F.add((i-1)*m+j+n*m,t,inf); } } } } } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>c; if(c=='L') { sum++; F.add(s,(i-1)*m+j,1); } } } cout<<sum-F.Dinic(s,t)<<endl; return 0; }
luogu P3324 [SDOI2015] 星际战争
-
二分答案。
点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; ll a[110],b[110],c[110][110]; struct MaxFlow { struct node { ll nxt,to,cap,flow; }e[5510]; ll head[110],dis[110],vis[110],cur[110],cnt=1; void clear() { memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); cnt=1; } void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } ll Dinic(ll s,ll t) { ll flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }F; bool check(ll mid,ll n,ll m) { F.clear(); ll s=m+n+1,t=m+n+2,sum=0; for(ll i=1;i<=n;i++) { sum+=a[i]; F.add(m+i,t,a[i]); } for(ll i=1;i<=m;i++) { F.add(s,i,mid*b[i]); for(ll j=1;j<=n;j++) { if(c[i][j]==1) F.add(i,m+j,inf); } } return F.Dinic(s,t)>=sum; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,l=0,r=1000000000000,mid,ans=0,i,j; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; a[i]*=10000; } for(i=1;i<=m;i++) { cin>>b[i]; } for(i=1;i<=m;i++) { for(j=1;j<=n;j++) cin>>c[i][j]; } while(l<=r) { mid=(l+r)/2; if(check(mid,n,m)==true) { ans=mid; r=mid-1; } else { l=mid+1; } } printf("%.3lf\n",ans/10000.0); return 0; }
luogu P4311 士兵占领
-
用上下界网络流有点杀鸡用牛刀了,考虑统计最多能少放几个士兵。
点击查看代码
const int inf=0x3f3f3f3f; int l[210],c[210],num[2][210],vis[210][210]; struct MaxFlow { struct node { int nxt,to,cap,flow; }e[21010]; int head[210],dis[210],vis[210],cur[210],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,k,s,t,x,y,flag=1,sum=0,i,j; cin>>n>>m>>k; s=n+m+1; t=n+m+2; for(i=1;i<=n;i++) cin>>l[i]; for(i=1;i<=m;i++) cin>>c[i]; for(i=1;i<=k;i++) { cin>>x>>y; vis[x][y]=1; num[0][x]++; num[1][y]++; } for(i=1;i<=n;i++) { F.add(s,i,m-num[0][i]-l[i]); if(m-num[0][i]<l[i]) flag=0; } for(i=1;i<=m;i++) { F.add(n+i,t,n-num[1][i]-c[i]); if(n-num[1][i]<c[i]) flag=0; } if(flag==1) { for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(vis[i][j]==0) { sum++; F.add(i,n+j,1); } } } cout<<sum-F.Dinic(s,t)<<endl; } else cout<<"JIONG!"<<endl; return 0; }
luogu P3191 [HNOI2007] 紧急疏散EVACUATE
-
同 luogu P2939 [USACO09FEB] Revamping Trails G ,考虑建分层图,在上一层的残量网络上继续转移。
点击查看代码
const int inf=0x3f3f3f3f,dx[4]={0,1,0,-1},dy[4]={1,0,-1,0}; int id[2][50][50][2],n,m,s=0,t=1,tot=1; char c[50][50]; struct MaxFlow { struct node { int nxt,to,cap,flow; }e[1300010]; int head[160010],vis[160010],dis[160010],cur[160010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }F; void build(int tim) { for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(c[i][j]=='X') continue; tot++; id[tim&1][i][j][0]=tot; tot++; id[tim&1][i][j][1]=tot; if(tim==0) { if(c[i][j]=='.') F.add(s,id[tim&1][i][j][0],1); else F.add(id[tim&1][i][j][1],t,1); F.add(id[tim&1][i][j][0],id[tim&1][i][j][1],1); } else { if(c[i][j]=='.') F.add(id[tim&1][i][j][0],id[tim&1][i][j][1],inf); else F.add(id[tim&1][i][j][0],t,1); F.add(id[(tim-1)&1][i][j][1],id[tim&1][i][j][0],inf); for(int k=0;k<=3;k++) { int nx=i+dx[k],ny=j+dy[k]; if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&c[nx][ny]=='.') F.add(id[(tim-1)&1][nx][ny][1],id[tim&1][i][j][0],inf); } } } } } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int flag=0,sum=0,i,j; cin>>n>>m; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>c[i][j]; sum+=(c[i][j]=='.'); } } for(i=0;i<=400;i++) { build(i); sum-=F.Dinic(s,t); if(sum<=0) { flag=1; cout<<i<<endl; break; } } if(flag==0) cout<<"impossible"<<endl; return 0; }
luogu P4843 清理雪道
-
每条边下界为
,上界为 。点击查看代码
const int inf=0x3f3f3f3f; struct UpDownMinFlow { struct node { int nxt,to,cap,flow; }e[25010]; int head[110],f[110],cur[110],dis[110],vis[110],cnt=1,val,limit; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } void _add(int u,int v,int up,int down) { add(u,v,up-down); f[u]-=down; f[v]+=down; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow&&i<=limit) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow&&i<=limit) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } bool check(int n,int s,int t) { int _s=n+1,_t=n+2,sum=0,tmp; _add(t,s,inf,0); tmp=head[t]; for(int i=1;i<=n;i++) { if(f[i]>0) { sum+=f[i]; add(_s,i,f[i]); } if(f[i]<0) add(i,_t,-f[i]); } limit=inf; sum-=Dinic(_s,_t); limit=tmp; val=e[limit].flow; e[limit].cap=e[limit].flow=e[limit^1].cap=e[limit^1].flow=0; return sum<=0; } int query(int s,int t) { return val-Dinic(t,s); } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,x,i,j; cin>>n; s=n+1; t=n+2; for(i=1;i<=n;i++) { F._add(s,i,inf,0); F._add(i,t,inf,0); cin>>m; for(j=1;j<=m;j++) { cin>>x; F._add(i,x,inf,1); } } F.check(n+2,s,t); cout<<F.query(s,t)<<endl; return 0; }
luogu P5038 [SCOI2012] 奇怪的游戏
-
考虑将相邻的点进行黑白染色。
-
设有
个黑点,权值和为 ;有 个白点,权值和为 ;最终都要变成 。 -
显然有
,在 时有 ,否则考虑二分答案并进行 。 -
变化量挂在源、汇点与其的连边上,判断是否满流。
点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f,dx[4]={0,1,0,-1},dy[4]={1,0,-1,0}; ll a[110][110]; pair<ll,ll>num[2]; struct MaxFlow { struct node { ll nxt,to,cap,flow; }e[16010]; ll head[1610],dis[1610],vis[1610],cur[1610],cnt=1; void clear() { memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); cnt=1; } void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } ll Dinic(ll s,ll t) { ll flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }F; bool check(ll mid,ll n,ll m) { F.clear(); ll s=n*m+1,t=n*m+2,sum=0; for(ll i=1;i<=n;i++) { for(ll j=1;j<=m;j++) { if((i+j)%2==0) { F.add(s,(i-1)*m+j,mid-a[i][j]); sum+=mid-a[i][j]; for(ll k=0;k<=3;k++) { ll nx=i+dx[k],ny=j+dy[k]; if(1<=nx&&nx<=n&&1<=ny&&ny<=m) F.add((i-1)*m+j,(nx-1)*m+ny,inf); } } else F.add((i-1)*m+j,t,mid-a[i][j]); } } return F.Dinic(s,t)==sum; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,m,l,r,ans,mid,i,j; cin>>t; for(;t>=1;t--) { cin>>n>>m; l=0; r=10000000000000; ans=-1; num[0]=num[1]=make_pair(0ll,0ll); for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>a[i][j]; l=max(l,a[i][j]); num[(i+j)%2].first+=a[i][j]; num[(i+j)%2].second++; } } if(num[0].second==num[1].second) { if(num[0].first==num[1].first) { while(l<=r) { mid=(l+r)/2; if(check(mid,n,m)==true) { ans=mid; r=mid-1; } else { l=mid+1; } } } } else { ans=(num[0].first-num[1].first)/(num[0].second-num[1].second); if(ans<l||check(ans,n,m)==false) ans=-1; } cout<<((ans==-1)?-1:ans*num[0].second-num[0].first)<<endl; } return 0; }
luogu P2756 飞行员配对方案问题
-
二分图最大匹配。构造方案仅需遍历除与源、汇点相连的边判断是否满流即可。
点击查看代码
const int inf=0x3f3f3f3f; struct MaxFlow { struct node { int nxt,to,cap,flow; }e[20010]; int head[110],dis[110],vis[110],cur[110],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } void print(int s,int t) { for(int i=2;i<=cnt;i+=2) { if(e[i].to!=s&&e[i].to!=t&&e[i^1].to!=s&&e[i^1].to!=t&&e[i].flow==1) { cout<<e[i^1].to<<" "<<e[i].to<<endl; } } } }F; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int m,n,s,t,u,v,i; cin>>m>>n; s=n+1; t=n+2; while(cin>>u>>v) { if(u==-1&&v==-1) break; F.add(u,v,1); } for(i=1;i<=m;i++) F.add(s,i,1); for(i=m+1;i<=n;i++) F.add(i,t,1); cout<<F.Dinic(s,t)<<endl; F.print(s,t); return 0; }
luogu P1646 [国家集训队] happiness
-
多倍经验: luogu P1361 小M的作物
-
从源点向第
种作物连一条容量为 的有向边,从第 种作物向汇点连一条容量为 的有向边。每一种割都对应了一种合法的构造方案。常用总量减去最小割(最小亏损)求得最大价值。 -
对额外收益建虚点转移即可。注意网络上容量为
的边不可能被割掉。点击查看代码
const int inf=0x3f3f3f3f; int tot; struct MinCut { struct node { int nxt,to,cap,flow; }e[300010]; int head[50010],dis[50010],vis[50010],cur[50010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,sum=0,s,t,x,i,j; cin>>n>>m; s=n*m+1; tot=t=n*m+2; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>x; sum+=x; C.add(s,(i-1)*m+j,x); } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>x; sum+=x; C.add((i-1)*m+j,t,x); } } for(i=1;i<=n-1;i++) { for(j=1;j<=m;j++) { cin>>x; sum+=x; tot++; C.add(s,tot,x); C.add(tot,(i-1)*m+j,inf); C.add(tot,i*m+j,inf); } } for(i=1;i<=n-1;i++) { for(j=1;j<=m;j++) { cin>>x; sum+=x; tot++; C.add(tot,t,x); C.add((i-1)*m+j,tot,inf); C.add(i*m+j,tot,inf); } } for(i=1;i<=n;i++) { for(j=1;j<=m-1;j++) { cin>>x; sum+=x; tot++; C.add(s,tot,x); C.add(tot,(i-1)*m+j,inf); C.add(tot,(i-1)*m+j+1,inf); } } for(i=1;i<=n;i++) { for(j=1;j<=m-1;j++) { cin>>x; sum+=x; tot++; C.add(tot,t,x); C.add((i-1)*m+j,tot,inf); C.add((i-1)*m+j+1,tot,inf); } } cout<<sum-C.Dinic(s,t)<<endl; return 0; }
luogu P2762 太空飞行计划问题
-
最大权闭合子图。源点向实验连边,仪器向汇点连边,保留原图中实验向汇点连边。
-
每一种割都对应一种选择方案(某条边在割上说明选中了其亏损),输出方案考虑构造割时取残量网络上能到达的点即可。
点击查看代码
const int inf=0x3f3f3f3f; char tools[10000]; struct MinCut { struct node { int nxt,to,cap,flow; }e[5510]; int head[110],dis[110],vis[110],cur[110],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,x,sum=0,i; cin>>m>>n; s=n+m+1; t=n+m+2; for(i=1;i<=m;i++) { cin>>x; sum+=x; C.add(s,i,x); memset(tools,0,sizeof tools); cin.getline(tools,10000); int ulen=0,tool; while (sscanf(tools+ulen,"%d",&tool)==1) { C.add(i,m+tool,inf); if (tool==0) ulen++; else { while (tool) { tool/=10; ulen++; } } ulen++; } } for(i=1;i<=n;i++) { cin>>x; C.add(m+i,t,x); } sum-=C.Dinic(s,t); for(i=1;i<=m;i++) { if(C.vis[i]==1) cout<<i<<" "; } cout<<endl; for(i=1;i<=n;i++) { if(C.vis[m+i]==1) cout<<i<<" "; } cout<<endl; cout<<sum<<endl; return 0; }
luogu P2057 [SHOI2007] 善意的投票 / [JLOI2010] 冠军调查
-
同意从源点连边,不同意从汇点连边,朋友间连双向边跑最小割。
点击查看代码
const int inf=0x3f3f3f3f; struct MinCut { struct node { int nxt,to,cap,flow; }e[210010]; int head[310],dis[310],vis[310],cur[310],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,x,y,i; cin>>n>>m; s=n+1; t=n+2; for(i=1;i<=n;i++) { cin>>x; if(x==1) C.add(s,i,1); else C.add(i,t,1); } for(i=1;i<=m;i++) { cin>>x>>y; C.add(x,y,1); C.add(y,x,1); } cout<<C.Dinic(s,t)<<endl; return 0; }
luogu P4174 [NOI2006] 最大获利
-
中转点和用户群间的容量为
。点击查看代码
const int inf=0x3f3f3f3f; struct MinCut { struct node { int nxt,to,cap,flow; }e[320010]; int head[60010],dis[60010],vis[60010],cur[60010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,x,y,z,sum=0,i; cin>>n>>m; s=n+m+1; t=n+m+2; for(i=1;i<=n;i++) { cin>>x; C.add(s,i,x); } for(i=1;i<=m;i++) { cin>>x>>y>>z; sum+=z; C.add(n+i,t,z); C.add(x,n+i,inf); C.add(y,n+i,inf); } cout<<sum-C.Dinic(s,t)<<endl; return 0; }
luogu P4177 [CEOI 2008] order
-
机器和工作的容量为
表示在割掉这条边后选择租用此机器,而不是购买。点击查看代码
const int inf=0x3f3f3f3f; struct MinCut { struct node { int nxt,to,cap,flow; }e[3000010]; int head[2510],dis[2510],vis[2510],cur[2510],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,x,y,a,b,sum=0,i,j; cin>>n>>m; s=m+n+1; t=m+n+2; for(i=1;i<=n;i++) { cin>>x>>y; sum+=x; C.add(s,m+i,x); for(j=1;j<=y;j++) { cin>>a>>b; C.add(m+i,a,b); } } for(i=1;i<=m;i++) { cin>>x; C.add(i,t,x); } cout<<sum-C.Dinic(s,t)<<endl; return 0; }
luogu P4781 【模板】拉格朗日插值
-
朴素拉格朗日插值板子。
个不同的点可以唯一确定一个 次多项式。- 考虑构造
个函数 使得 过 ,那么所求 。 - 设
,将 代入后解得 ,即 。 - 合并起来得到
,朴素实现的时间复杂度为 ,可以优化到 。
点击查看代码
const ll p=998244353; ll x[2010],y[2010]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } ll lagrange(ll n,ll _x) { ll ans=0; for(ll i=1;i<=n;i++) { ll up=1,down=1; for(ll j=1;j<=n;j++) { if(i!=j) { up=up*(_x-x[j]+p)%p; down=down*(x[i]-x[j]+p)%p; } } ans=(ans+y[i]*up%p*qpow(down,p-2,p)%p)%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,k,i; cin>>n>>k; for(i=1;i<=n;i++) cin>>x[i]>>y[i]; cout<<lagrange(n,k)<<endl; return 0; }
CF622F The Sum of the k-th Powers
-
是一个 次多项式。 -
横坐标是连续整数的拉格朗日插值板子。
- 假设我们已知
,要求出 这个 次多项式。 - 分子
可以拆成前后缀积拼起来计算,也可以直接拿 计算。 - 分母
拆成前后阶乘的两部分 。 - 时间复杂度为
。
点击查看代码
const ll p=1000000007; ll x[1000010],y[1000010],pre[1000010],suf[1000010],inv[1000010],jc_inv[1000010]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } ll lagrange(ll n,ll _x) { ll ans=0; pre[0]=suf[n+2]=1; for(ll i=1;i<=n+1;i++) pre[i]=pre[i-1]*(_x-x[i]+p)%p; for(ll i=n+1;i>=1;i--) suf[i]=suf[i+1]*(_x-x[i]+p)%p; inv[1]=jc_inv[0]=jc_inv[1]=1; for(ll i=2;i<=n+1;i++) { inv[i]=inv[p%i]*(p-p/i)%p; jc_inv[i]=jc_inv[i-1]*inv[i]%p; } for(ll i=1;i<=n+1;i++) { if((n+1-i)%2==0) ans=(ans+jc_inv[i-1]*jc_inv[n+1-i]%p*pre[i-1]%p*suf[i+1]%p*y[i]%p)%p; else ans=(ans-jc_inv[i-1]*jc_inv[n+1-i]%p*pre[i-1]%p*suf[i+1]%p*y[i]%p+p)%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,k,i; cin>>n>>k; for(i=1;i<=k+2;i++) { x[i]=i; y[i]=(y[i-1]+qpow(i,k,p))%p; } cout<<(n<=k+2?y[n]:lagrange(k+1,n))<<endl; return 0; }
- 假设我们已知
LibreOJ 165. 拉格朗日插值
-
重心拉格朗日插值板子。
- 每次求点值都暴力代入不可接受,考虑进一步优化。
- 将分子化成
的形式。 - 设
,原式等于 。 - 每加入一个点就
更新 ,询问点值 回答。
点击查看代码
const ll p=998244353; ll x[3010],y[3010],c[3010],n; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } void insert(ll _x,ll _y) { n++; x[n]=_x; y[n]=c[n]=_y; for(ll i=1;i<=n-1;i++) { c[i]=c[i]*qpow((x[i]-x[n]+p)%p,p-2,p)%p; c[n]=c[n]*qpow((x[n]-x[i]+p)%p,p-2,p)%p; } } ll lagrange(ll _x) { ll t=1,sum=0; for(ll i=1;i<=n;i++) { if(_x==x[i]) return y[i]; t=t*(_x-x[i]+p)%p; sum=(sum+c[i]*qpow((_x-x[i]+p)%p,p-2,p)%p)%p; } return t*sum%p; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll m,pd,_x,_y,i; cin>>m; for(i=1;i<=m;i++) { cin>>pd>>_x; if(pd==1) { cin>>_y; insert(_x,_y); } else cout<<lagrange(_x)<<endl; } return 0; }
luogu P7112 【模板】行列式求值
-
的方阵 的行列式 可以理解为所有列向量所夹的几何体的有向体积。 -
其计算公式为
,其中 是全体长度为 的排列构成的集合, 表示排列 的逆序对数。 -
性质
- 转置(行、列交换)后行列式不变,即
。- 行、列的地位是等价的。
- 交换不同的的两行/列,行列式变号,需要乘以
。 - 若存在某一行/列全为
或存在不同的两行/列全部对应相等,则行列式为 。 - 某一行的值都乘上
后,行列式等比例变化。 - 若存在不同的两行/列全部对应成比例,则行列式为
。 - 单位矩阵的行列式为
。 - 把某一行/列的元素乘以同一数后加到另一行/列的对应元素上,行列式不变。
- 若
第 行的所有元素都可以表示成两项的和,即 时有 ,其中 的第 行是 , 的第 行是 ,其余各行都和 相同。列同理。 - 下三角方阵的行列式为对角线乘积。
- 转置(行、列交换)后行列式不变,即
-
考虑对方阵进行高斯消元直至为下三角矩阵。
-
本题中因涉及取模且不保证存在逆元,需要进行辗转相减,途中反转行列式的符号。均摊后的时间复杂度为
。点击查看代码
ll a[610][610],p; ll det(ll n) { ll ans=1,op=0; for(ll i=1;i<=n;i++) { for(ll j=i+1;j<=n;j++) { while(a[i][i]!=0) { ll div=a[j][i]/a[i][i]; for(ll k=i;k<=n;k++) a[j][k]=(a[j][k]-div*a[i][k]%p+p)%p;//消元 swap(a[i],a[j]); op^=1;//辗转相减 } swap(a[i],a[j]); op^=1; } //中途可以在 a[i][i]=0 时直接 return 0 ans=ans*a[i][i]%p; } return (op==0)?ans:(p-ans)%p; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,i,j; cin>>n>>p; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) cin>>a[i][j]; } cout<<det(n)<<endl; return 0; }
luogu P3973 [TJOI2015] 线性代数
-
将所求的式子展开,有
。 -
将
分别看做贡献和代价跑最小割,建虚点。点击查看代码
const int inf=0x3f3f3f3f; int b[510][510],c[510]; struct MinCut { struct node { int nxt,to,cap,flow; }e[1600010]; int head[260010],dis[260010],vis[260010],cur[260010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,s,t,sum=0,i,j; cin>>n; s=n*n+n+1; t=n*n+n+2; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { cin>>b[i][j]; sum+=b[i][j]; C.add(s,(i-1)*n+j,b[i][j]); C.add((i-1)*n+j,n*n+i,inf); C.add((i-1)*n+j,n*n+j,inf); } } for(i=1;i<=n;i++) { cin>>c[i]; C.add(n*n+i,t,c[i]); } cout<<sum-C.Dinic(s,t)<<endl; return 0; }
luogu P1791 [国家集训队] 人员雇佣
-
同时雇佣和不同时雇佣的利润差为
,则 间容量为 。点击查看代码
const ll inf=0x3f3f3f3f3f3f3f; ll a[1010],b[1010][1010]; struct MinCut { struct node { ll nxt,to,cap,flow; }e[2100010]; ll head[1010],dis[1010],vis[1010],cur[1010],cnt=1; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } ll Dinic(ll s,ll t) { ll flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,s,t,sum=0,i,j; cin>>n; s=n+1; t=n+2; for(i=1;i<=n;i++) { cin>>a[i]; C.add(s,i,a[i]); } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { cin>>b[i][j]; sum+=b[i][j]; b[i][0]+=b[i][j]; C.add(i,j,2*b[i][j]); } } for(i=1;i<=n;i++) C.add(i,t,b[i][0]); cout<<sum-C.Dinic(s,t)<<endl; return 0; }
BZOJ3158 千钧一发
-
两个条件都不满足的进行连边。
-
不满足第二个条件的只有可能是奇数和奇数或奇数和偶数。而奇数和奇数的平方和展开后无法进行找到相应的平方数,无法进行连边。于是可以只考虑奇数和偶数的情况。
-
建二分图中间连
后跑最小割。点击查看代码
const ll inf=0x3f3f3f3f3f3f3f; ll a[1010],b[1010]; struct MinCut { struct node { ll nxt,to,cap,flow; }e[2100010]; ll head[1010],dis[1010],vis[1010],cur[1010],cnt=1; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } ll Dinic(ll s,ll t) { ll flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; bool check(ll a,ll b) { ll c=sqrt(a*a+b*b); return a*a+b*b==c*c&&__gcd(a,b)==1&&a%2!=b%2; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,s,t,sum=0,i,j; cin>>n; s=n+1; t=n+2; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=n;i++) { cin>>b[i]; sum+=b[i]; if(a[i]%2==1) C.add(s,i,b[i]); else C.add(i,t,b[i]); for(j=1;j<=n;j++) { if(i!=j&&check(a[i],a[j])==true) C.add(((a[i]%2==1)?i:j),((a[i]%2==0)?i:j),inf); } } cout<<sum-C.Dinic(s,t)<<endl; return 0; }
2.8
闲话
- 上午
打 accoders NOI 的模拟赛。 - 下午没有体活。
- 晚上
说明天早上正常体活,但是只能睡觉,不能在外面活动。
做题纪要
luogu P2763 试题库问题
-
二分图最大匹配。
点击查看代码
const int inf=0x3f3f3f3f; int a[1210]; struct MinCut { struct node { int nxt,to,cap,flow; }e[80010]; int head[1210],dis[1210],vis[1210],cur[1210],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } void print(int x,int n) { for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].flow!=0&&e[i].to<=n) cout<<e[i].to<<" "; } } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int m,n,s,t,k,x,sum=0,i,j; cin>>m>>n; s=n+m+1; t=n+m+2; for(i=1;i<=m;i++) { cin>>a[i]; sum+=a[i]; C.add(n+i,t,a[i]); } for(i=1;i<=n;i++) { cin>>k; C.add(s,i,1); for(j=1;j<=k;j++) { cin>>x; C.add(i,n+x,1); } } if(C.Dinic(s,t)==sum) { for(i=1;i<=m;i++) { cout<<i<<": "; C.print(n+i,n); cout<<endl; } } else cout<<"No Solution!"<<endl; return 0; }
P906. 电车
Gym103438I Flood Fill
-
相邻两个极大不同色连通块反转两次没什么意义,不如直接限制只能选一个或都不选来反转。
-
间的边容量为 表示不能割掉。点击查看代码
const int inf=0x3f3f3f3f,dx[4]={1,0,-1,0},dy[4]={0,-1,0,1}; int a[1010][1010],b[1010][1010],id[1000010],tot; struct MinCut { struct node { int nxt,to,cap,flow; }e[13000010]; int head[1000010],dis[1000010],vis[1000010],cur[1000010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; struct DSU { int fa[1000010],siz[2][1000010]; void init(int n) { for(int i=1;i<=n;i++) fa[i]=i; } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void merge(int x,int y) { x=find(x); y=find(y); if(x!=y) { fa[x]=y; siz[0][y]+=siz[0][x]; siz[1][y]+=siz[1][x]; } } }D; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,x,y,nx,ny,i,j,k; char c; cin>>n>>m; s=1; tot=t=2; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>c; a[i][j]=c-'0'; } } D.init(n*m); for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>c; b[i][j]=c-'0'; D.siz[0][(i-1)*m+j]=(a[i][j]==b[i][j]); D.siz[1][(i-1)*m+j]=(a[i][j]!=b[i][j]); } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { for(k=0;k<=3;k++) { nx=i+dx[k]; ny=j+dy[k]; if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&a[i][j]==a[nx][ny]) D.merge((i-1)*m+j,(nx-1)*m+ny); } } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(D.fa[(i-1)*m+j]==(i-1)*m+j) { tot++; id[(i-1)*m+j]=tot; C.add(s,id[(i-1)*m+j],D.siz[a[i][j]^1][(i-1)*m+j]); C.add(id[(i-1)*m+j],t,D.siz[a[i][j]][(i-1)*m+j]); } } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { x=D.find((i-1)*m+j); for(k=0;k<=3;k++) { nx=i+dx[k]; ny=j+dy[k]; if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&a[i][j]!=a[nx][ny]) { y=D.find((nx-1)*m+ny); if(a[i][j]==0) C.add(id[x],id[y],inf); else C.add(id[y],id[x],inf); } } } } cout<<C.Dinic(s,t)<<endl; return 0; }
luogu P2754 [CTSC1999] 家园 / 星际转移问题
-
分层图上跑最大流。
点击查看代码
const int inf=0x3f3f3f3f; int h[50],id[2][50],n,m,s,t,tot; vector<int>c[50]; struct MaxFlow { struct node { int nxt,to,cap,flow; }e[600010]; int head[50010],vis[50010],dis[50010],cur[50010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }F; void build(int tim,int sum) { for(int i=0;i<=n+1;i++) { tot++; id[tim&1][i]=tot; if(tim!=0) F.add(id[(tim-1)&1][i],id[tim&1][i],inf); } if(tim==0) F.add(s,id[tim&1][0],sum); F.add(id[tim&1][n+1],t,inf); for(int i=1;i<=m&&tim!=0;i++) F.add(id[(tim-1)&1][c[i][(tim-1)%c[i].size()]],id[tim&1][c[i][tim%c[i].size()]],h[i]); } struct DSU { int fa[50]; void init(int n) { for(int i=0;i<=n;i++) fa[i]=i; } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void merge(int x,int y) { x=find(x); y=find(y); if(x!=y) fa[x]=y; } }D; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int sum,x,y,i,j; cin>>n>>m>>sum; s=1; t=tot=2; D.init(n+1); for(i=1;i<=m;i++) { cin>>h[i]>>y; for(j=1;j<=y;j++) { cin>>x; if(x==-1) x=n+1; if(c[i].empty()==0) D.merge(c[i].back(),x); c[i].push_back(x); } } if(D.find(0)==D.find(n+1)) { for(i=0;;i++) { build(i,sum); sum-=F.Dinic(s,t); if(sum<=0) { cout<<i<<endl; break; } } } else cout<<0<<endl; return 0; }
luogu P2153 [SDOI2009] 晨跑
-
把边拆成点后跑最小费用最大流。
点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; struct MaxFlow_MinCost { struct node { ll nxt,to,w,cap,flow; }e[60010]; ll head[510],vis[510],cur[510],dis[510],cnt=1,flow,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); queue<ll>q; dis[s]=0; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]!=inf; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; vis[x]=1; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } void Dinic(ll s,ll t) { flow=cost=0; while(spfa(s,t)==true) { for(ll x=dfs(s,t,inf);x!=0;x=dfs(s,t,inf)) flow+=x; } } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,w,i; cin>>n>>m; s=1; t=n; for(i=1;i<=m;i++) { cin>>u>>v>>w; if(u==1) C.add(u,v,1,w); else C.add(u+n,v,1,w); } for(i=2;i<=n-1;i++) C.add(i,i+n,1,0); C.Dinic(s,t); cout<<C.flow<<" "<<C.cost<<endl; return 0; }
luogu P4068 [SDOI2016] 数字配对
-
按照质因数个数奇偶性建二分图跑最大费用最大流。
-
当前求得的最大费用加入总费用后仍
时后续答案也 ,尽可能多地流后及时退出。点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; ll a[410],b[410],c[410],d[410]; struct MaxFlowMaxCost { struct node { ll nxt,to,w,cap,flow; }e[80010]; ll head[410],dis[410],vis[410],pre[410],incf[410],cnt=1,flow,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { for(ll i=1;i<=t;i++) { vis[i]=0; dis[i]=-inf; } queue<ll>q; dis[s]=0; incf[s]=inf; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]<dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; pre[e[i].to]=i; incf[e[i].to]=min(incf[x],e[i].cap-e[i].flow); if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]!=-inf; } bool update(ll s,ll t) { if(cost+incf[t]*dis[t]>=0) { flow+=incf[t]; cost+=incf[t]*dis[t]; for(ll x=t;x!=s;x=e[pre[x]^1].to) { e[pre[x]].flow+=incf[t]; e[pre[x]^1].flow-=incf[t]; } return true; } else { flow+=cost/(-dis[t]); return false; } } ll Edmonds_Krap(ll s,ll t) { flow=cost=0; while(spfa(s,t)==true&&update(s,t)==true); return flow; } }C; ll divide(ll n) { ll ans=0; for(ll i=2;i<=sqrt(n);i++) { while(n%i==0) { ans++; n/=i; } } if(n>1) ans++; return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,s,t,i,j; cin>>n; s=n+1; t=n+2; for(i=1;i<=n;i++) { cin>>a[i]; d[i]=divide(a[i]); } for(i=1;i<=n;i++) { cin>>b[i]; if(d[i]%2==1) C.add(s,i,b[i],0); else C.add(i,t,b[i],0); } for(i=1;i<=n;i++) cin>>c[i]; for(i=1;i<=n;i++) { if(d[i]%2==1) { for(j=1;j<=n;j++) { if((a[i]%a[j]==0&&d[i]==d[j]+1)||(a[j]%a[i]==0&&d[j]==d[i]+1)) C.add(i,j,inf,c[i]*c[j]); } } } cout<<C.Edmonds_Krap(s,t)<<endl; return 0; }
P904. 雪雀
P905. 燕鸥
2.9
闲话
- 早上有体活。
- 上午
打 @jijidawang | @wang54321 | @xrlong 组的模拟赛。 - 下午把昨天的题和今天的题一起讲了。
- 晚上
说明天没有模拟赛,之前和我们说的让准备分享的题我们都准备了吗。
做题纪要
luogu P3355 骑士共存问题
-
多倍经验: luogu P2774 方格取数问题 | luogu P4474 王者之剑 | luogu P10939 骑士放置 | luogu P5030 长脖子鹿放置
-
按照下标奇偶性建二分图。
-
二分图最大独立集等于点数减最小点覆盖数等于点数减最小割。
点击查看代码
const int inf=0x3f3f3f3f,dx[8]={2,1,-1,-2,-2,-1,1,2},dy[8]={-1,-2,-2,-1,1,2,2,1}; int vis[210][210]; struct MinCut { struct node { int nxt,to,cap,flow; }e[640010]; int head[40010],dis[40010],vis[40010],cur[40010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,x,y,i,j,k; cin>>n>>m; s=n*n+1; t=n*n+2; for(i=1;i<=m;i++) { cin>>x>>y; vis[x][y]=1; } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if((i+j)%2==0) { if(vis[i][j]==0) C.add(s,(i-1)*n+j,1); for(k=0;k<=7;k++) { x=i+dx[k]; y=j+dy[k]; if(1<=x&&x<=n&&1<=y&&y<=n&&vis[x][y]==0) C.add((i-1)*n+j,(x-1)*n+y,inf); } } else if(vis[i][j]==0) C.add((i-1)*n+j,t,1); } } cout<<n*n-m-C.Dinic(s,t)<<endl; return 0; }
luogu P2045 方格取数加强版
-
拆成入点和出点后跑最大费用最大流。
点击查看代码
const int inf=0x3f3f3f3f; int a[60][60]; struct MaxFlowMaxCost { struct node { int nxt,to,w,cap,flow; }e[20010]; int head[5010],dis[5010],vis[5010],cur[5010],cnt=1,cost; void add(int u,int v,int w,int _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(int s,int t) { memset(dis,-0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=0; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); vis[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]<dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]>0; } int dfs(int x,int t,int flow) { if(x==t) return flow; vis[x]=1; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } int Dinic(int s,int t) { cost=0; while(spfa(s,t)==true) for(int x=dfs(s,t,inf);x!=0;x=dfs(s,t,inf)); return cost; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,k,s,t,i,j; cin>>n>>k; s=1; t=2*n*n; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { cin>>a[i][j]; C.add((i-1)*n+j,(i-1)*n+j+n*n,1,a[i][j]); C.add((i-1)*n+j,(i-1)*n+j+n*n,k-1,0); if(i+1<=n) C.add((i-1)*n+j+n*n,i*n+j,k,0); if(j+1<=n) C.add((i-1)*n+j+n*n,(i-1)*n+j+1,k,0); } } cout<<C.Dinic(s,t)<<endl; return 0; }
luogu P1251 餐巾计划问题
-
把每天拆成早上和晚上,把需用餐巾数看做流量跑最小费用最大流。
点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; ll a[2010]; struct MaxFlowMinCost { struct node { ll nxt,to,w,cap,flow; }e[48010]; ll head[4010],dis[4010],cur[4010],vis[4010],cnt=1,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=0; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]<inf; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; vis[x]=1; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } ll Dinic(ll s,ll t) { cost=0; while(spfa(s,t)==true) while(dfs(s,t,inf)!=0); return cost; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,s,t,p,t1,c1,t2,c2,i; cin>>n; s=2*n+1; t=2*n+2; for(i=1;i<=n;i++) { cin>>a[i]; C.add(s,i+n,a[i],0); C.add(i,t,a[i],0); } cin>>p>>t1>>c1>>t2>>c2; for(i=1;i<=n;i++) { C.add(s,i,inf,p); if(i+1<=n) C.add(i+n,i+1+n,inf,0); if(i+t1<=n) C.add(i+n,i+t1,inf,c1); if(i+t2<=n) C.add(i+n,i+t2,inf,c2); } cout<<C.Dinic(s,t)<<endl; return 0; }
luogu P3227 [HNOI2013] 切糕
-
距离限制模型。
- 首先有点权转边权跑最小割。
的限制等价于不能同时选择 ,即同时割掉它们时 仍能到达 ,考虑从 向 连一条容量为 的边进行限制。
点击查看代码
const int inf=0x3f3f3f3f,dx[4]={1,0,-1,0},dy[4]={0,-1,0,1}; int a[50][50][50],id[50][50][50],tot; struct MinCut { struct node { int nxt,to,cap,flow; }e[640010]; int head[70010],dis[70010],vis[70010],cur[70010],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,r,d,s,t,x,y,i,j,k,h; cin>>n>>m>>r>>d; s=1; tot=t=2; for(i=1;i<=r+1;i++) { for(j=1;j<=n;j++) { for(k=1;k<=m;k++) { tot++; id[i][j][k]=tot; if(i<=r) cin>>a[i][j][k]; else { C.add(s,id[1][j][k],inf); C.add(id[r+1][j][k],t,inf); } if(i>=2) C.add(id[i-1][j][k],id[i][j][k],a[i-1][j][k]); } } } for(i=d+1;i<=r+1;i++) { for(j=1;j<=n;j++) { for(k=1;k<=m;k++) { for(h=0;h<=3;h++) { x=j+dx[h]; y=k+dy[h]; if(1<=x&&x<=n&&1<=y&&y<=m) C.add(id[i][j][k],id[i-d][x][y],inf); } } } } cout<<C.Dinic(s,t)<<endl; return 0; }
luogu P2517 [HAOI2010] 订货
-
和 luogu P1251 餐巾计划问题 一样做即可。
点击查看代码
const ll inf=0x3f3f3f3f3f3f3f3f; ll a[2010],d[2010]; struct MaxFlowMinCost { struct node { ll nxt,to,w,cap,flow; }e[48010]; ll head[4010],dis[4010],cur[4010],vis[4010],cnt=1,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=0; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]<inf; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; vis[x]=1; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } ll Dinic(ll s,ll t) { cost=0; while(spfa(s,t)==true) while(dfs(s,t,inf)!=0); return cost; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,k,s,t,i; cin>>n>>m>>k; s=n+1; t=n+2; for(i=1;i<=n;i++) { cin>>a[i]; C.add(i,t,a[i],0); } for(i=1;i<=n;i++) { cin>>d[i]; C.add(s,i,inf,d[i]); if(i+1<=n) C.add(i,i+1,k,m); } cout<<C.Dinic(s,t)<<endl; return 0; }
luogu P2053 [SCOI2007] 修车
-
同 luogu P1223 排队接水 ,考虑将等待时间的贡献拆开。
-
将每个技术人员拆成若干个点,第
个点表示修倒数第 辆车时,贡献为 。点击查看代码
const int inf=0x3f3f3f3f; int a[70][70]; struct MaxFlowMinCost { struct node { int nxt,to,w,cap,flow; }e[4100010]; int head[35010],dis[35010],vis[35010],cur[35010],cnt=1,cost; void add(int u,int v,int w,int _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(int s,int t) { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); vis[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]<inf; } int dfs(int x,int t,int flow) { if(x==t) return flow; vis[x]=1; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } int Dinic(int s,int t) { cost=0; while(spfa(s,t)==true) while(dfs(s,t,inf)!=0); return cost; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,i,j,k; cin>>m>>n; s=n+n*m+1; t=n+n*m+2; for(i=1;i<=n;i++) { C.add(n*m+i,t,1,0); for(j=1;j<=m;j++) { cin>>a[i][j]; for(k=1;k<=n;k++) C.add((j-1)*n+k,n*m+i,1,a[i][j]*k); } } for(i=1;i<=n*m;i++) C.add(s,i,1,0); printf("%.2lf\n",1.0*C.Dinic(s,t)/n); return 0; }
luogu P2050 [NOI2012] 美食节
-
的边数和 的点数无法通过。 -
观察到增广过程中很多点是无用的,故每增广一层对菜品往外新建与厨师的连边。通过 Edmonds_Krap 找到任意一条增广路后取上面任意一个厨师即可。
点击查看代码
const int inf=0x3f3f3f3f; int a[110][110],b[110]; struct MaxFlowMinCost { struct node { int nxt,to,w,cap,flow; }e[7000010]; int head[81040],dis[81040],vis[81040],incf[81040],pre[81040],cnt=1,cost; void add(int u,int v,int w,int _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(int s,int t) { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=0; incf[s]=inf; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); vis[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; pre[e[i].to]=i; incf[e[i].to]=min(incf[x],e[i].cap-e[i].flow); if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]<inf; } void update(int s,int t) { for(int x=t;x!=s;x=e[pre[x]^1].to) { e[pre[x]].flow+=incf[t]; e[pre[x]^1].flow-=incf[t]; } cost+=incf[t]*dis[t]; } int Edmonds_Krap(int s,int t,int n,int m,int sum) { cost=0; while(spfa(s,t)==true) { update(s,t); int x=e[pre[t]^1].to; add(x+1,t,1,0); for(int i=1;i<=n;i++) add(sum*m+i,x+1,1,a[i][(int)ceil(1.0*x/sum)]*(x%sum+1)); } return cost; } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,t,sum=0,i,j; cin>>n>>m; for(i=1;i<=n;i++) { cin>>b[i]; sum+=b[i]; } s=sum*m+n+1; t=sum*m+n+2; for(i=1;i<=n;i++) { C.add(s,sum*m+i,b[i],0); for(j=1;j<=m;j++) { cin>>a[i][j]; C.add(sum*m+i,(j-1)*sum+1,1,a[i][j]); } } for(i=1;i<=m;i++) C.add((i-1)*sum+1,t,1,0); cout<<C.Edmonds_Krap(s,t,n,m,sum)<<endl; return 0; }
UVA10480 Sabotag
-
最小割输出方案。
点击查看代码
struct MinCut { struct node { ll nxt,to,cap,flow; }e[2010]; ll head[60],cur[60],vis[60],dis[60],cnt=1; void clear() { memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); cnt=1; } void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } bool bfs(ll s,ll t) { memset(vis,0,sizeof(vis)); queue<ll>q; dis[s]=1; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); cur[x]=head[x]; for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; ll sum=0,tmp; for(ll i=cur[x];i!=0;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } void Dinic(ll s,ll t) { while(bfs(s,t)==true) dfs(s,t,0x3f3f3f3f3f3f3f3f); } void print() { for(ll i=2;i<=cnt;i+=4) { if(vis[e[i].to]!=vis[e[i^1].to]) { cout<<e[i].to<<" "<<e[i^1].to<<endl; } } } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,s,t,u,v,w,i; while(cin>>n>>m) { C.clear(); s=1; t=2; for(i=1;i<=m;i++) { cin>>u>>v>>w; C.add(u,v,w); C.add(v,u,w); } C.Dinic(s,t); C.print(); cout<<endl; } return 0; }
P938. 渗透 B20
2.10
闲话
- 下午的时候
让我和 @jijidawang 把分享的题和大家说了一下。 说让我们好好珍惜自己和别人分享的好题,不断累积自己手中的资源。 - 晚新闻的时候我讲 CF757G Can Bash Save the Day? 他讲 CF1864H Asterism Stream 。
做题纪要
luogu P4014 分配问题
-
多倍经验: luogu P4015 运输问题
-
费用流。
点击查看代码
struct MaxFlow_MinCost { const ll inf=0x3f3f3f3f3f3f3f3f; struct node { ll nxt,to,w,cap,flow; }e[100010]; ll head[5010],vis[5010],cur[5010],dis[5010],cnt=1,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); queue<ll>q; dis[s]=0; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return (dis[t]!=inf); } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; vis[x]=1; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } ll Dinic(ll s,ll t) { cost=0; while(spfa(s,t)==true) while(dfs(s,t,inf)!=0); return cost; } }N; struct MaxFlow_MaxCost { const ll inf=0x3f3f3f3f3f3f3f3f; struct node { ll nxt,to,w,cap,flow; }e[100010]; ll head[5010],vis[5010],cur[5010],dis[5010],cnt=1,cost; void add(ll u,ll v,ll w,ll _w) { cnt++; e[cnt]=(node){head[u],v,_w,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,-_w,0,0}; head[v]=cnt; } bool spfa(ll s,ll t) { memset(vis,0,sizeof(vis)); memset(dis,-0x3f,sizeof(dis)); queue<ll>q; dis[s]=0; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { ll x=q.front(); q.pop(); vis[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]<dis[x]+e[i].w&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+e[i].w; cur[e[i].to]=head[e[i].to]; if(vis[e[i].to]==0) { q.push(e[i].to); vis[e[i].to]=1; } } } } return dis[t]>0; } ll dfs(ll x,ll t,ll flow) { if(x==t) return flow; vis[x]=1; ll sum=0,tmp; for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; cost+=tmp*e[i].w; } } vis[x]=0; return sum; } ll Dinic(ll s,ll t) { cost=0; while(spfa(s,t)==true) while(dfs(s,t,inf)!=0); return cost; } }X; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,s,t,x,i,j; cin>>n; s=2*n+1; t=2*n+2; for(i=1;i<=n;i++) { N.add(s,i,1,0); N.add(i+n,t,1,0); X.add(s,i,1,0); X.add(i+n,t,1,0); for(j=1;j<=n;j++) { cin>>x; N.add(i,n+j,1,x); X.add(i,n+j,1,x); } } cout<<N.Dinic(s,t)<<endl<<X.Dinic(s,t)<<endl; return 0; }
luogu P3803 【模板】多项式乘法(FFT)
-
复数
-
复数
的模定义为复数在复平面( 轴为实轴, 轴为虚轴)对应的向量的模,即 。 -
复数满足加法交换律和加法结合率,乘法交换律、乘法结合率和对加法的分配率。
-
复数除法常进行分母实母化,即
- 其中
称为 的共轭复数,记为 。复数共轭满足 。
- 其中
-
欧拉公式:
。- 复指数函数
,其满足 。 是以 为基本周期的周期函数。
- 复三角函数
。特别地,在 时,由欧拉公式有 。
- 复指数函数
-
称
在复数意义下的解是 次复根,这 个解都是 次单位(复)根,这些根把单位圆 等分。- 设
(即幅角为 的单位复数),则 的解集表示为 。- 由周期理论可知
。
- 由周期理论可知
- 若不加说明,一般提到的
次单位根是指从 开始逆时针方向的第一个解 ,其他解均可以用 的幂表示。 - 称集合
中的元素为本原单位根,借助任意一个本原单位根,都可以生成全体单位根。全体 次本原单位根共有 个。这与原根的性质很相似。
- 设
-
-
-
多项式
- 多项式卷积
- 以多项式乘法为例,有
。 - 更为普遍的卷积形式是
。
- 以多项式乘法为例,有
- FFT 和 NTT 能够加速
为加法时的运算,时间复杂度为 。-
FFT
- 基本思想是将相乘的两个多项式
化作点值表示法的形式后,将对应数字相乘,再还原到系数表示法。简单拉来说就是先求值再插值。 - DFT 用于将系数表示法化为点值表示法。
- 基本思想是通过分治将
代入多项式 求得对应的值。具体地,通过对次数的奇偶进行分组建立新的函数递归求解。注意 DFT 只能处理长度为 的整数次幂的多项式,如次数不够则应先进行补位。 - 形式化地,将式子化成
的形式后记 , 得到 。 - 代入
后分别得到 。分别对 进行递归分治求解即可。 - 具体实现时,需要顺次代入
,其中 是 的整数次幂。可以使用 STL 的complex
调用exp
函数求对应的 ,也可以通过欧拉公式去求。后者可以进行预处理 ,常数较小。 - 分治的常数有点大,一般采用倍增迭代实现。具体地,先模拟递归的划分方式把这些系数在原数组中划分到逐层统计答案时对应的位置上,然后再进行倍增合并。
- 通过位逆序置换实现拆分。具体地,观察到划分本质上是在逐渐去除二进制下最后一位后按照末尾进行排序,即将其二进制反转对称。
for(int i=0;i<limit;i++) rev[i]=(rev[i>>1]>>1)+(i&1)*(limit>>1);
- 可以采用蝶形运算优化常数,直接在原位置进行覆写,减小因数组拷贝的常数。
- 基本思想是通过分治将
- IDFT 用于将点值表示法化为系数表示法。
- 考虑 DFT 本质上是一个线性变换,矩阵表示形如
- 此时只需要在式子连边同时乘以中间矩阵的逆矩阵即可。又因为这个矩阵里的元素非常特殊,其逆矩阵就是每一项取倒数后除以变换长度
,具体证明考虑等比数列求和即可。 - 由
,将此时的单位根 取成 后与 DFT 流程一样。 - 可以在最后再统一除以
。
- 考虑 DFT 本质上是一个线性变换,矩阵表示形如
点击查看分治代码
const double Pi=acos(-1); struct sx_complex { double x,y; sx_complex(double _x=0,double _y=0) { x=_x; y=_y; } sx_complex operator + (const sx_complex &another) const { return {x+another.x,y+another.y}; } sx_complex operator - (const sx_complex &another) const { return {x-another.x,y-another.y}; } sx_complex operator * (const sx_complex &another) const { return {x*another.x-y*another.y,x*another.y+y*another.x}; } }f[2200010],g[2200010],tmp[2200010]; struct polynomial { int limit; int init(int deg) { for(limit=1;limit<=deg;limit<<=1);//保证长度 > deg,计数 >=deg return limit; } void fft(sx_complex f[],int l,int r,int op) { if(l==r) return; int mid=(l+r)/2,len=r-l+1; for(int i=l;i<=r;i++) tmp[i]=f[i]; for(int i=0;i<len/2;i++) { f[i+l]=tmp[i*2+l]; f[i+mid+1]=tmp[i*2+l+1]; } fft(f,l,mid,op); fft(f,mid+1,r,op); sx_complex w={1,0},base={cos(2*Pi/len),sin(2*Pi*op/len)},cur; for(int i=0;i<len/2;i++,w=w*base)//蝶形优化减小数组拷贝常数 { cur=w*f[i+mid+1]; f[i+mid+1]=f[i+l]-cur; f[i+l]=f[i+l]+cur; } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,limit,i; scanf("%d%d",&n,&m); limit=P.init(n+m); for(i=0;i<=n;i++) scanf("%lf",&f[i].x); for(i=0;i<=m;i++) scanf("%lf",&g[i].x); P.fft(f,0,limit-1,1); P.fft(g,0,limit-1,1); for(i=0;i<limit;i++) f[i]=f[i]*g[i]; P.fft(f,0,limit-1,-1); for(i=0;i<=n+m;i++) printf("%d ",(int)(f[i].x/limit+0.5));//除以变换长度 return 0; }
点击查倍增迭代代码
const double Pi=acos(-1); struct sx_complex { double x,y; sx_complex(double _x=0,double _y=0) { x=_x; y=_y; } sx_complex operator + (const sx_complex &another) const { return {x+another.x,y+another.y}; } sx_complex operator - (const sx_complex &another) const { return {x-another.x,y-another.y}; } sx_complex operator * (const sx_complex &another) const { return {x*another.x-y*another.y,x*another.y+y*another.x}; } }f[2200010],g[2200010]; struct polynomial { int rev[2200010],limit; sx_complex mi[2200010]; int init(int deg) { mi[1]={1,0}; for(limit=1;limit<=deg;limit<<=1,mi[limit]={cos(2*Pi/limit),sin(2*Pi/limit)});//预处理单位根 for(int i=0;i<limit;i++) rev[i]=(rev[i>>1]>>1)+(i&1)*(limit>>1); return limit; } void fft(sx_complex f[],int op) { for(int i=0;i<limit;i++) { if(i<rev[i]) swap(f[i],f[rev[i]]); } for(int half=1,len=2;len<=limit;half<<=1,len<<=1) { sx_complex base={mi[len].x,mi[len].y*op},w,cur; for(int l=0;l<limit;l+=len) { w={1,0}; for(int i=l;i<l+half;i++,w=w*base)//蝶形优化减少额外空间开销 { cur=w*f[i+half]; f[i+half]=f[i]-cur; f[i]=f[i]+cur; } } } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,limit,i; scanf("%d%d",&n,&m); limit=P.init(n+m); for(i=0;i<=n;i++) scanf("%lf",&f[i].x); for(i=0;i<=m;i++) scanf("%lf",&g[i].x); P.fft(f,1); P.fft(g,1); for(i=0;i<limit;i++) f[i]=f[i]*g[i]; P.fft(f,-1); for(i=0;i<=n+m;i++) printf("%d ",(int)(f[i].x/limit+0.5)); return 0; }
- 针对于本题,可以使用三次变两次优化。具体地,构造复数多项式
,那么 即为所求。 - 部分时候精度误差较大,谨慎使用。
点击查看三次变两次代码
const double Pi=acos(-1); struct sx_complex { double x,y; sx_complex(double _x=0,double _y=0) { x=_x; y=_y; } sx_complex operator + (const sx_complex &another) const { return {x+another.x,y+another.y}; } sx_complex operator - (const sx_complex &another) const { return {x-another.x,y-another.y}; } sx_complex operator * (const sx_complex &another) const { return {x*another.x-y*another.y,x*another.y+y*another.x}; } }f[2200010]; struct polynomial { int rev[2200010],limit; sx_complex mi[2200010]; int init(int deg) { mi[1]={1,0}; for(limit=1;limit<=deg;limit<<=1,mi[limit]={cos(2*Pi/limit),sin(2*Pi/limit)}); for(int i=0;i<limit;i++) rev[i]=(rev[i>>1]>>1)+(i&1)*(limit>>1); return limit; } void fft(sx_complex f[],int op) { for(int i=0;i<limit;i++) { if(i<rev[i]) swap(f[i],f[rev[i]]); } for(int half=1,len=2;len<=limit;half<<=1,len<<=1) { sx_complex base={mi[len].x,mi[len].y*op},w,cur; for(int l=0;l<limit;l+=len) { w={1,0}; for(int i=l;i<l+half;i++,w=w*base) { cur=w*f[i+half]; f[i+half]=f[i]-cur; f[i]=f[i]+cur; } } } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,limit,i; scanf("%d%d",&n,&m); limit=P.init(n+m); for(i=0;i<=n;i++) scanf("%lf",&f[i].x); for(i=0;i<=m;i++) scanf("%lf",&f[i].y); P.fft(f,1); for(i=0;i<limit;i++) f[i]=f[i]*f[i]; P.fft(f,-1); for(i=0;i<=n+m;i++) printf("%d ",(int)(f[i].y/2/limit+0.5)); return 0; }
- 基本思想是将相乘的两个多项式
-
NTT
- 单位根处的复数运算导致了较大的常数,且经常遇到需要取模的问题, FFT 有时就不能很好的处理了。
- 上文提到了本原单位根部分方面与原根很相似,启发我们进一步探寻本原单位根与原根之间的替代关系。
- 具体地,将
替换成 ,执行 IDFT 时将 替换成 。 - 但是其限制了
必须为整数,即 一定是 的形式,其中 要保证 。常见的 NTT 模数及原根 。
点击查看代码
const int p=998244353,phi=998244352,g=3,inv_g=332748118; int f[2][2200010]; int qpow(int a,int b,int p) { int ans=1; while(b) { if(b&1) ans=1ll*ans*a%p; b>>=1; a=1ll*a*a%p; } return ans; } struct polynomial { int rev[2200010],limit; int init(int deg) { for(limit=1;limit<=deg;limit<<=1); for(int i=0;i<limit;i++) rev[i]=(rev[i>>1]>>1)+(i&1)*(limit>>1); return limit; } void ntt(int f[],int op) { for(int i=0;i<limit;i++) { if(i<rev[i]) swap(f[i],f[rev[i]]); } for(int half=1,len=2;len<=limit;half<<=1,len<<=1) { int base=qpow((op==1)?g:inv_g,phi/len,p),w,cur; for(int l=0;l<limit;l+=len) { w=1; for(int i=l;i<l+half;i++,w=1ll*w*base%p) { cur=1ll*w*f[i+half]%p; f[i+half]=(f[i]-cur+p)%p; f[i]=(f[i]+cur)%p; } } } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,limit,inv,i; scanf("%d%d",&n,&m); limit=P.init(n+m); inv=qpow(limit,p-2,p); for(i=0;i<=n;i++) scanf("%d",&f[0][i]); for(i=0;i<=m;i++) scanf("%d",&f[1][i]); P.ntt(f[0],1); P.ntt(f[1],1); for(i=0;i<limit;i++) f[0][i]=1ll*f[0][i]*f[1][i]%p; P.ntt(f[0],-1); for(i=0;i<=n+m;i++) printf("%d ",1ll*f[0][i]*inv%p); return 0; }
-
- 多项式卷积
luogu P1919 【模板】高精度乘法 | A*B Problem 升级版
-
令
后使用 FFT 加速。点击查看代码
const double Pi=acos(-1); int ans[3000010]; char s[3000010],t[3000010]; struct sx_complex { double x,y; sx_complex(double _x=0,double _y=0) { x=_x; y=_y; } sx_complex operator + (const sx_complex &another) const { return {x+another.x,y+another.y}; } sx_complex operator - (const sx_complex &another) const { return {x-another.x,y-another.y}; } sx_complex operator * (const sx_complex &another) const { return {x*another.x-y*another.y,x*another.y+y*another.x}; } }f[3000010]; struct polynomial { int rev[3000010],limit; sx_complex mi[3000010]; int init(int deg) { mi[1]={1,0}; for(limit=1;limit<=deg;limit<<=1,mi[limit]={cos(2*Pi/limit),sin(2*Pi/limit)}); for(int i=0;i<limit;i++) rev[i]=(rev[i>>1]>>1)+((i&1)?(limit>>1):0); return limit; } void fft(sx_complex f[],int op) { for(int i=0;i<limit;i++) { if(i<rev[i]) swap(f[i],f[rev[i]]); } for(int half=1,len=2;len<=limit;half<<=1,len<<=1) { sx_complex base={mi[len].x,mi[len].y*op},w,cur; for(int l=0;l<limit;l+=len) { w={1,0}; for(int i=l;i<l+half;i++,w=w*base) { cur=w*f[i+half]; f[i+half]=f[i]-cur; f[i]=f[i]+cur; } } } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,limit,i; cin>>s>>t; n=strlen(s)-1; m=strlen(t)-1; for(i=0;i<=n;i++) f[i].x=s[n-i]-'0'; for(i=0;i<=m;i++) f[i].y=t[m-i]-'0'; limit=P.init(n+m); P.fft(f,1); for(i=0;i<limit;i++) f[i]=f[i]*f[i]; P.fft(f,-1); for(i=0;i<limit;i++) { ans[i]+=f[i].y/2/limit+0.5; ans[i+1]+=ans[i]/10; ans[i]%=10; } while(limit>1&&ans[limit]==0) limit--; for(i=limit;i>=0;i--) cout<<ans[i]; return 0; }
luogu P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)
-
FWT 能够加速
为位运算的卷积。其同样要求长度为 的整数次幂。 -
核心思想是构造
后进行反演得到 ,其中 表示对应位置相乘。- 按位或
- 定义
。显然有 。 - 然后快速地对
进行求值和插值。 - 求值考虑分治进行合并两个区间的同时统计新加入最高位的贡献,有
,其中 表示像字符串拼接一样把两个数组拼起来, 表示对应位置相加。 - 插值考虑合并时剔除最高位的贡献(类似回退背包的写法),有
。 - 为方便代码书写和减小常数,使用倍增迭代实现,并使用蝶形优化。
- 定义
- 按位与
- 定义
。有 。 - 求值时,有
。 - 插值时,有
。
- 定义
- 异或
- 定义
表示 。按位逐层考虑有 。 - 定义
。 - 求值时
中的元素最高位为 ,而 中的元素最高位为 ,有 。 - 插值时,有
。
- 定义
- 同或
- 定义
表示 。 - 定义
。 - 求值时
中的元素最高位为 ,而 中的元素最高位为 ,有 。 - 插值时,有
。
- 定义
点击查看代码
const int p=998244353; int a[150010],b[150010],c[150010]; int qpow(int a,int b,int p) { int ans=1; while(b) { if(b&1) ans=1ll*ans*a%p; b>>=1; a=1ll*a*a%p; } return ans; } struct polynomial { int limit; void init(int n) { limit=1<<n; } void fwt_or(int f[],int op) { for(int half=1,len=2;len<=limit;half<<=1,len<<=1) { for(int l=0;l<limit;l+=len) { for(int i=l;i<l+half;i++) f[i+half]=(f[i+half]+1ll*f[i]*op%p)%p; } } } void fwt_and(int f[],int op) { for(int half=1,len=2;len<=limit;half<<=1,len<<=1) { for(int l=0;l<limit;l+=len) { for(int i=l;i<l+half;i++) f[i]=(f[i]+1ll*f[i+half]*op%p)%p; } } } void fwt_xor(int f[],int op) { for(int half=1,len=2;len<=limit;half<<=1,len<<=1) { for(int l=0;l<limit;l+=len) { for(int i=l;i<l+half;i++) { int cur=f[i]; f[i]=1ll*(f[i]+f[i+half])*op%p; f[i+half]=1ll*(cur-f[i+half]+p)*op%p; } } } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,inv=qpow(2,p-2,p),i; cin>>n; P.init(n); for(i=0;i<(1<<n);i++) cin>>a[i]; for(i=0;i<(1<<n);i++) cin>>b[i]; P.fwt_or(a,1); P.fwt_or(b,1); for(i=0;i<(1<<n);i++) c[i]=1ll*a[i]*b[i]%p; P.fwt_or(a,p-1); P.fwt_or(b,p-1); P.fwt_or(c,p-1); for(i=0;i<(1<<n);i++) cout<<c[i]<<" "; cout<<endl; P.fwt_and(a,1); P.fwt_and(b,1); for(i=0;i<(1<<n);i++) c[i]=1ll*a[i]*b[i]%p; P.fwt_and(a,p-1); P.fwt_and(b,p-1); P.fwt_and(c,p-1); for(i=0;i<(1<<n);i++) cout<<c[i]<<" "; cout<<endl; P.fwt_xor(a,1); P.fwt_xor(b,1); for(i=0;i<(1<<n);i++) c[i]=1ll*a[i]*b[i]%p; P.fwt_xor(a,inv); P.fwt_xor(b,inv); P.fwt_xor(c,inv); for(i=0;i<(1<<n);i++) cout<<c[i]<<" "; cout<<endl; return 0; }
- 按位或
-
从另一个角度重新描述
的过程。- 设
表示 对 的贡献系数,此时有 。 - 可以证明
,其中 为位运算。 - 将
拆成 两半,对应位置下只有最高位不同。我们只需要 这个位矩阵即可实现求值,视 最高位决定取第一行还是第二行。插值同理,但需要保证有逆矩阵。 - 按位或
的位矩阵和逆矩阵分别为 。 - 按位与
的位矩阵和逆矩阵分别为 。 - 异或
的位矩阵和逆矩阵分别为 。 - 同或
的位矩阵和逆矩阵分别为 。
- 设
UVA13277 XOR Path
-
将根链边权异或和作为下标后 FWT 加速卷积。
点击查看代码
struct node { ll nxt,to,w; }e[200010]; ll head[100010],dis[100010],a[150010],c[150010],cnt=0; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w}; head[u]=cnt; } void dfs(ll x,ll fa) { a[dis[x]]++; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dis[e[i].to]=dis[x]^e[i].w; dfs(e[i].to,x); } } } struct polynomial { ll limit; void init(ll n) { limit=1<<n; } void fwt(ll f[],ll op) { for(ll half=1,len=2;len<=limit;half<<=1,len<<=1) { for(ll l=0;l<limit;l+=len) { for(ll i=l;i<l+half;i++) { ll cur=f[i]; f[i]+=f[i+half]; f[i+half]=cur-f[i+half]; if(op==-1) { f[i]/=2; f[i+half]/=2; } } } } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,u,v,w,i,j; cin>>t; P.init(16); for(j=1;j<=t;j++) { cnt=0; memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); memset(a,0,sizeof(a)); cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } dfs(1,0); P.fwt(a,1); for(i=0;i<(1<<16);i++) c[i]=a[i]*a[i]; P.fwt(c,-1); cout<<"Case "<<j<<":"<<endl; c[0]-=n; for(i=0;i<(1<<16);i++) cout<<c[i]/2<<endl; } return 0; }
luogu P4245 【模板】任意模数多项式乘法
-
因不保证模数有原根,不能直接套用 NTT 。
-
观察到系数理论最大值
,一般取三个相乘后 级别的模数 分别求出对应答案后和 一起做 CRT 合并。 -
不妨手动进行 CRT 合并。
-
设
,易得 ,其中 。 -
再次合并得到
,其中 。 -
因所求
,和 直接可以无脑合并,取 作为答案即可。点击查看代码
const ll mod[4]={0,469762049,998244353,1004535809},phi[4]={0,469762048,998244352,1004535808},g=3, inv_g[4]={0,156587350,332748118,334845270},inv[3]={0,554580198,395249030}; ll mul=468937312667959297; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } struct quality { ll x,y,z; quality(ll _x=0,ll _y=0,ll _z=0) { x=_x; y=_y; z=_z; } quality operator + (const quality &another) { return {(x+another.x)%mod[1], (y+another.y)%mod[2], (z+another.z)%mod[3]}; } quality operator - (const quality &another) { return {(x-another.x+mod[1])%mod[1], (y-another.y+mod[2])%mod[2], (z-another.z+mod[3])%mod[3]}; } quality operator * (const quality &another) { return {x*another.x%mod[1], y*another.y%mod[2], z*another.z%mod[3]}; } ll crt(ll p) { ll r=(y-x+mod[2])%mod[2]*inv[1]%mod[2]*mod[1]+x; ll k=(z-r%mod[3]+mod[3])%mod[3]*inv[2]%mod[3]%p; return (k*mul%p+r%p)%p; } }f[2][280010]; struct polynomial { ll rev[280010],limit; ll init(ll deg) { for(limit=1;limit<=deg;limit<<=1); for(ll i=0;i<limit;i++) rev[i]=(rev[i>>1]>>1)+(i&1)*(limit>>1); return limit; } void ntt(quality f[],ll op) { for(ll i=0;i<limit;i++) { if(i<rev[i]) swap(f[i],f[rev[i]]); } for(ll half=1,len=2;len<=limit;half<<=1,len<<=1) { quality base,w,cur; if(op==1) base={qpow(g,phi[1]/len,mod[1]), qpow(g,phi[2]/len,mod[2]), qpow(g,phi[3]/len,mod[3])}; else base={qpow(inv_g[1],phi[1]/len,mod[1]), qpow(inv_g[2],phi[2]/len,mod[2]), qpow(inv_g[3],phi[3]/len,mod[3])}; for(ll l=0;l<limit;l+=len) { w={1,1,1}; for(ll i=l;i<l+half;i++,w=w*base) { cur=w*f[i+half]; f[i+half]=f[i]-cur; f[i]=f[i]+cur; } } } if(op==-1) { quality inv={qpow(limit,mod[1]-2,mod[1]), qpow(limit,mod[2]-2,mod[2]), qpow(limit,mod[3]-2,mod[3])}; for(ll i=0;i<limit;i++) f[i]=f[i]*inv; } } }P; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,p,limit,i; cin>>n>>m>>p; limit=P.init(n+m); mul%=p; for(i=0;i<=n;i++) { cin>>f[0][i].x; f[0][i].x%=p; f[0][i].y=f[0][i].z=f[0][i].x; } for(i=0;i<=m;i++) { cin>>f[1][i].x; f[1][i].x%=p; f[1][i].y=f[1][i].z=f[1][i].x; } P.ntt(f[0],1); P.ntt(f[1],1); for(i=0;i<limit;i++) f[0][i]=f[0][i]*f[1][i]; P.ntt(f[0],-1); for(i=0;i<=n+m;i++) cout<<f[0][i].crt(p)<<" "; return 0; }
luogu P4897 【模板】最小割树(Gomory-Hu Tree)
-
最小割树。
- 考虑分治建最小割树,对于一段区间
内的点,任取 ,然后求出 之间的最小割,将其作为 两点间的边权。 然后将 分成 所属的两个割,分别进行递归求解。 - 任意两点
间的最小割等于路径上边权最小值。询问时再树上倍增没什么意义,不如之间 预处理 查询。 - 注意每次求最小割时都要在原图上跑,而不是在上一层从残量网络上跑。
点击查看代码
const int inf=0x3f3f3f3f; struct MinCutTree { struct node { int nxt,to,cap,flow; }e[6010]; int head[510],dis[510],vis[510],cur[510],id[510],tmp[510],ans[510][510],cnt=1; void add(int u,int v,int w) { cnt++; e[cnt]=(node){head[u],v,w,0}; head[u]=cnt; cnt++; e[cnt]=(node){head[v],u,0,0}; head[v]=cnt; } void init(int n) { for(int i=0;i<=n;i++) id[i]=i; memset(ans,0x3f,sizeof(ans)); } bool bfs(int s,int t) { memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=1; cur[s]=head[s]; q.push(s); vis[s]=1; while(q.empty()==0) { int x=q.front(); q.pop(); for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0&&e[i].cap>e[i].flow) { dis[e[i].to]=dis[x]+1; cur[e[i].to]=head[e[i].to]; q.push(e[i].to); vis[e[i].to]=1; if(e[i].to==t) return true; } } } return false; } int dfs(int x,int t,int flow) { if(x==t) return flow; int sum=0,tmp; for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt) { cur[x]=i; if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow) { tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum)); if(tmp==0) dis[e[i].to]=0; sum+=tmp; e[i].flow+=tmp; e[i^1].flow-=tmp; } } return sum; } int Dinic(int s,int t) { for(int i=2;i<=cnt;i++) e[i].flow=0; int flow=0; while(bfs(s,t)==true) flow+=dfs(s,t,inf); return flow; } void build(int l,int r) { if(l>=r) return; int s=id[l],t=id[l+1],flow=Dinic(s,t),x=l-1,y=r+1; for(int i=l;i<=r;i++) { if(vis[id[i]]==1) { x++; tmp[x]=id[i]; } else { y--; tmp[y]=id[i]; } } for(int i=l;i<=r;i++) id[i]=tmp[i]; build(l,x); build(y,r); for(int i=l;i<=x;i++) { for(int j=y;j<=r;j++) ans[id[i]][id[j]]=ans[id[j]][id[i]]=min({ans[id[i]][s],ans[t][id[j]],flow}); } } }C; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,q,u,v,w,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>u>>v>>w; C.add(u,v,w); C.add(v,u,w); } C.init(n); C.build(0,n); cin>>q; for(i=1;i<=q;i++) { cin>>u>>v; cout<<C.ans[u][v]<<endl; } return 0; }
- 考虑分治建最小割树,对于一段区间
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18696366,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2024-02-02 2024初三年前集训测试2