T1:
别被定义弄晕了
反着做,A->1/A+B
取倒数没法做,所以变成a/b,维护2*2的矩阵
区间?不用线段树,不用倍增
存在逆矩阵,直接前缀积
注意左乘右乘方向
T2
模拟费用流
经典老鼠和洞的问题
序列:从左往右扫,
到了老鼠i,wi加入堆,
到了洞j,找权值最大的未匹配老鼠或者已经匹配的洞k,如果valk+vj>0,更新ans,pop,然后把-vj加入堆
外向树:可并堆进行上述操作
而本题带修
(52)60pts做法:
相当于模拟动态加边费用流,
建图方式就是树上直接建,S到miner,gold到T
加入一个miner,会和匹配的miner、未匹配的gold产生匹配
疯狂DFS走有流量的边到这些点,选择最大的
加入gold,和未匹配的miner、匹配的gold产生匹配
这里DFS,如果反向边有流量才走,因为实际在反向增广
如果有匹配并且贡献>0,暴力把整个路径流量改变
匹配和未匹配的miner和gold用2个堆维护
由于可能加入的费用过大,会造成正环
一般机械费用流要消正环
但是其实无所谓,正环只会产生在miner-S-miner之间,以及gold-T-gold之间
和我们的人工方法一致
#include#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< using namespace std;typedef long long ll;template 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 il void output(T x){ if(x/10)output(x/10);putchar(x%10+'0');}template il void ot(T x){ if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}template il void prt(T a[],int st,int nd){ for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}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 il int ad(const int a,const int b,const Args &...args) { return ad(ad(a,b),args...);}template 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 q[N][2];//0: matched miner ; no match gold//1: matched gold ; no match minerstruct 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
100pts:
miner往上跳到最高点,子树最大值就是答案
gold就是到根的链的所有轻儿子连通块内的最大值
树剖维护一下,找连通块,线段树pushup时候考虑中间边有没有,就好了
在写了在写了
T3:
好题
https://loj.ac/article/1779
没有大于号限制是好算的.
容斥的话,枚举i最后连续的不满足的大于号
分治NTT
dp0=1.题解打错了