LibreOJ NOI Round #2 Day 1
T1:
别被定义弄晕了
反着做,A->1/A+B
取倒数没法做,所以变成a/b,维护2*2的矩阵
区间?不用线段树,不用倍增
存在逆矩阵,直接前缀积
注意左乘右乘方向
T2
模拟费用流
经典老鼠和洞的问题
序列:从左往右扫,
到了老鼠i,wi加入堆,
到了洞j,找权值最大的未匹配老鼠或者已经匹配的洞k,如果valk+vj>0,更新ans,pop,然后把-vj加入堆
外向树:可并堆进行上述操作
而本题带修
(52)60pts做法:
https://www.luogu.org/blog/i207M/libreoj-noi-round-2-xie-ti-bao-gao
相当于模拟动态加边费用流,
建图方式就是树上直接建,S到miner,gold到T
加入一个miner,会和匹配的miner、未匹配的gold产生匹配
疯狂DFS走有流量的边到这些点,选择最大的
加入gold,和未匹配的miner、匹配的gold产生匹配
这里DFS,如果反向边有流量才走,因为实际在反向增广
如果有匹配并且贡献>0,暴力把整个路径流量改变
匹配和未匹配的miner和gold用2个堆维护
由于可能加入的费用过大,会造成正环
一般机械费用流要消正环
但是其实无所谓,正环只会产生在miner-S-miner之间,以及gold-T-gold之间
和我们的人工方法一致
#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){ char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);} template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar(' ');} namespace Modulo{ const int mod=998244353; il int ad(int x,int y){return x+y>=mod?x+y-mod:x+y;} il int sub(int x,int y){return ad(x,mod-y);} il int mul(int x,int y){return (ll)x*y%mod;} il void inc(int &x,int y){x=ad(x,y);} il void inc2(int &x,int y){x=mul(x,y);} il int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;} template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);} template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);} } //using namespace Modulo; namespace Miracle{ const int N=100000+5; const int inf=0x3f3f3f3f; int n,m; ll ans; priority_queue<ll>q[N][2]; //0: matched miner ; no match gold //1: matched gold ; no match miner struct node{ int nxt,to; int w,c; }e[2*N]; int du[N]; int hd[N],cnt=1; void add(int x,int y,int w,int c){ e[++cnt].nxt=hd[x]; e[cnt].to=y;e[cnt].w=w;e[cnt].c=c; hd[x]=cnt; e[++cnt].nxt=hd[y]; e[cnt].to=x;e[cnt].w=0;e[cnt].c=-c; hd[y]=cnt; } int pre[N]; ll mx; int id; void dfs(int x,int fa,ll d,int tp){ if(!q[x][tp].empty()){ if(q[x][tp].top()+d>mx){ mx=q[x][tp].top()+d; id=x; } } for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa) continue; if(tp==0){ if(e[i].w) pre[y]=i,dfs(y,x,d+e[i].c,tp); }else{ if(e[i^1].w) pre[y]=i,dfs(y,x,d+e[i^1].c,tp); } } } void wrk(int x,int v,int tp){ mx=1ll*-0x3f3f3f3f3f3f3f3f,id=0; dfs(x,0,0,tp); if(mx+v>0){ ans+=mx+v; ll val=q[id][tp].top(); q[id][tp].pop(); q[id][tp^1].push(-val); q[x][tp].push(-v); if(tp==0){ while(id!=x){ e[pre[id]].w--; e[pre[id]^1].w++; id=e[pre[id]^1].to; } }else{ while(id!=x){ e[pre[id]].w++; e[pre[id]^1].w--; id=e[pre[id]^1].to; } } }else{ q[x][tp^1].push(v); } } int main(){ rd(n);rd(m); int x,y,z; for(reg i=1;i<n;++i){ rd(x);rd(y);rd(z); add(x,y,inf,z); ++du[y]; } // int rt=0; //for(reg i=1;i<=n;++i){if(du[i]==0) rt=i;} int op,v; while(m--){ rd(op);rd(x);rd(v); --op; wrk(x,v,op); printf("%lld ",ans); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* */
100pts:
miner往上跳到最高点,子树最大值就是答案
gold就是到根的链的所有轻儿子连通块内的最大值
树剖维护一下,找连通块,线段树pushup时候考虑中间边有没有,就好了
在写了在写了
T3:
好题
https://loj.ac/article/1779
没有大于号限制是好算的.
容斥的话,枚举i最后连续的不满足的大于号
分治NTT
dp0=1.题解打错了