来自FallDream的博客。未经允许,请勿转载,谢谢。
----------------------------------------------------
A.积分,不会 以后补
B.给定一个n*m的矩阵,每个点是0或者1,然后q个操作,每次把一个位置取反,然后询问最大的全是1的正方形的边长。n*m<=4000000 , q<=3900
题解:我们用一个线段树把所有行维护起来,然后每一行对每一列维护这个行区间的这一列的u和d,u表示从最下面一个最多有多少个连续的1,d表示从上面网下面最多有多少个连续的1,这两个参数是很好合并的。然后我们考虑计算过中线的答案。我们用两个指针ij,再用两个单调队列,维护u和d单调下降。每当 i - j + 1> u[tail] + d[tail] 的时候,我们把j指针往前移。最后我们用i - j + 1更新答案。
复杂度qmlogn 如果n<m我们把nm交换一下。
最近被奇怪的代码风格洗脑了,写了一个不像是我写的程序(听我解释,我真的不是抄的.....) 其实我真正的代码风格像C题那样就是一团垃圾
#include<iostream> #include<cstdio> #define MN 2000 #define TEST using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } inline int cread() { char ch = getchar(); while(ch != '.' && ch != 'X') ch = getchar(); return ch == '.' ? 1 : 0; } int n , m , q , qx[MN + 5] , ltop , qy[MN + 5] , rtop , ltail , rtail; bool *s[MN + 5] , rev; struct Segment_Tree{ int ans , w; int *u , *d; Segment_Tree *l , *r; void init(int x) { ans = 0; for(int i = 1 ; i <= n ; ++i) u[i] = d[i] = s[i][x] , ans |= s[i][x]; } void update() { ans = max ( l->ans , r->ans ); int x , k = 1; for(int i = 1 ; i <= n ; ++i) { u[i] = l -> d[i]; d[i] = r -> u[i]; } qx[ltop = 1] = qy[rtop = 1] = 1; ltail = rtail = 1; for ( int i = 2; i <= n ; ++i) { while(u[i] <= u[qx[ltop]] && ltop >= ltail) ltop --; while(d[i] <= d[qy[rtop]] && rtop >= rtail) rtop --; qx[++ ltop] = i; qy[++ rtop] = i; while(i - k + 1> u[qx[ltail]] + d[qy[rtail]] && i > k) { ++ k; if(qx[ltail] < k) ltail ++; if(qy[rtail] < k) rtail ++; } ans = max(ans , i - k + 1); } for(int i = 1 ; i <= n ; ++i) { u[i] = ( l -> u[i] == l -> w) ? r -> u[i] + l -> w : l -> u[i]; d[i] = ( r -> d[i] == r -> w) ? l -> d[i] + r -> w : r -> d[i]; } } void modify(int x , int lt = 1 , int rt = m) { if(lt == rt) { init(x); return;} int mid = lt + rt >> 1; if(x <= mid) l -> modify(x , lt , mid); else r -> modify(x , mid + 1, rt); update(); } Segment_Tree(int lt , int rt ) { u = new int [n + 2]; d = new int [n + 2]; w = rt - lt + 1; if(lt == rt) { init(lt); return;} int mid = lt + rt >> 1; l = new Segment_Tree(lt , mid); r = new Segment_Tree(mid + 1 , rt); update(); } }*rt; int main() { #ifdef TEST freopen("s.in" , "r" , stdin); freopen("s.out" , "w" , stdout); #endif n = read(); m = read(); q = read(); if( n > m) swap(n , m),rev = true; for(int i = 1; i <= n ; ++i) s[i] = new bool [m + 2]; if(!rev) for(int i = 1; i <= n ; ++i) for(int j = 1; j <= m ; ++j) s[i][j] = cread(); else for(int i = 1; i <= m ; ++i) for(int j = 1; j<= n ; ++j) s[j][i] = cread(); rt = new Segment_Tree(1 , m); for(int i = 1; i <= q ; ++i) { int x = read() , y = read(); if(rev) swap(x , y); s[x][y] ^= 1; rt -> modify(y); printf("%d " , rt -> ans); } return 0; }
C.给定一棵n个点树,每个点可以涂成白的或者黑的。给定b条链和b个权值bi,表示这条链上的点全是黑色时候能得到bi的贡献。还有a条链和a个权值,表示全部涂成白色能得到的贡献。你要求出最大贡献。n<=100000 a,b<=30000 3s
题解:神题
最小割,很显然黑白链就是一个二分图。但是直接建图是不行的,所以我们考虑优化一下,把树拿去树剖一下,然后建两棵线段树,一棵只能往下走,一棵只能往上走。每条链对两棵线段树上的对应的log个区间建边,最后跑一次最小割。复杂度很玄学,就算O(能过)吧。
ditoly真的强,居然在现场想出了这个做法...%%%
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define N 800000 #define MAXL 10000000 #define MV 860000 #define MAXN 100000 #define MN 100000 #define S 0 #define tt 860001 #define INF 2000000000 inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int dn=0,cnt=0,ans=0,n,B,W,head2[MAXN+5],head[MV+5],top[MAXN+5],qx[MV+5],qtop,tail; int dfn[MAXN+5],mx[MAXN+5],size[MAXN+5],dep[MAXN+5],fa[MAXN+5],q[MV+5],tp[MV+5]; struct edge{ int to,next,w; }e[MAXL*2+5]; struct edge2{ int to,next; }e2[MAXN*2+5]; struct TREE{ int l,r; }T[MN*4+5]; inline void ins(int f,int t){e2[++cnt].next=head2[f];head2[f]=cnt;e2[cnt].to=t;} inline void ins(int f,int t,int w){e[++cnt].next=head[f];head[f]=cnt;e[cnt].to=t;e[cnt].w=w;} inline void insw(int f,int t,int w){ins(f,t,w);ins(t,f,0);} void dfs(int x,int f) { size[x]=1;mx[x]=0;fa[x]=f;int maxn=0; for(int i=head2[x];i;i=e2[i].next)if(e2[i].to!=f) { dep[e2[i].to]=dep[x]+1;dfs(e2[i].to,x); size[x]+=size[e2[i].to]; if(size[e2[i].to]>maxn){maxn=size[e2[i].to];mx[x]=e2[i].to;} } } void dfs2(int x,int t) { top[x]=t;dfn[x]=++dn; if(mx[x])dfs2(mx[x],t); for(int i=head2[x];i;i=e2[i].next)if(!dfn[e2[i].to]) {dfs2(e2[i].to,e2[i].to);} } void build(int x,int l,int r) { T[x].l=l;T[x].r=r;if(l==r)return; int mid=(l+r)>>1; insw(x,x<<1,INF);insw(x,x<<1|1,INF); insw((x<<1)+4*MN,x+4*MN,INF);insw((x<<1|1)+4*MN,x+4*MN,INF); build(x<<1,l,mid);build(x<<1|1,mid+1,r); } void insert(int from,int x,int l,int r) { // cout<<"insert"<<from<<" "<<x<<" "<<l<<" "<<r<<" "<<lt<<" "<<rt<<endl; if(T[x].l==l&&T[x].r==r){insw(from,x,INF);insw(from,x+MN*4,INF);return;} int mid=(T[x].l+T[x].r)>>1; if(r<=mid)insert(from,x<<1,l,r); else if(l>mid)insert(from,x<<1|1,l,r); else {insert(from,x<<1,l,mid);insert(from,x<<1|1,mid+1,r);} } void insert2(int to,int x,int l,int r) { // cout<<"insert2"<<to<<" "<<x<<" "<<l<<" "<<r<<" "<<lt<<" "<<rt<<endl; if(T[x].l==l&&T[x].r==r){insw(x,to,INF);insw(x+MN*4,to,INF);return;} int mid=(T[x].l+T[x].r)>>1; if(r<=mid)insert2(to,x<<1,l,r); else if(l>mid)insert2(to,x<<1|1,l,r); else {insert2(to,x<<1,l,mid);insert2(to,x<<1|1,mid+1,r);} } bool bfs() { memset(q,0,sizeof(q));int i,j; for(q[qx[qtop=i=0]=S]=1;i<=qtop;i++) for(int j=tp[qx[i]]=head[qx[i]];j;j=e[j].next)if(!q[e[j].to]&&e[j].w) q[qx[++qtop]=e[j].to]=q[qx[i]]+1; return q[tt]>0; } int solve(int x,int f) { // cout<<"solve"<<x<<" "<<f<<endl; if(x==tt)return f;int used=0; for(int&i=tp[x];i;i=e[i].next) if(e[i].w&&q[e[i].to]==q[x]+1) { int w=solve(e[i].to,min(f-used,e[i].w)); used+=w;e[i].w-=w;e[i^1].w+=w; if(e[i].w) tp[x]=i; if(used==f)return f; } if(!used)q[x]=-1; return used; } int main() { freopen("t.in","r",stdin); freopen("t.out","w",stdout); n=read();B=read();W=read(); for(int i=1;i<n;i++) {int u=read(),v=read();ins(u,v),ins(v,u);} cnt=1;dfs(1,0);dfs2(1,1); build(1,1,n); for(int i=1;i<=B;++i) { int u=read(),v=read(),x=read();insw(S,N+i,x);ans+=x; while(top[u]!=top[v]) { // cout<<u<<" "<<v<<" "<<top[u]<<" "<<top[v]<<endl; if(dep[top[u]]>dep[top[v]]) {insert(N+i,1,dfn[top[u]],dfn[u]);u=fa[top[u]];} else {insert(N+i,1,dfn[top[v]],dfn[v]);v=fa[top[v]];} } if(dfn[u]>dfn[v])swap(u,v);insert(N+i,1,dfn[u],dfn[v]); } for(int i=1;i<=W;++i) { int u=read(),v=read(),x=read();insw(N+i+B,tt,x);ans+=x; while(top[u]!=top[v]) { // cout<<u<<" "<<v<<" "<<top[u]<<" "<<top[v]<<endl; if(dep[top[u]]>dep[top[v]]) {insert2(N+i+B,1,dfn[top[u]],dfn[u]);u=fa[top[u]];} else {insert2(N+i+B,1,dfn[top[v]],dfn[v]);v=fa[top[v]];} } if(dfn[u]>dfn[v])swap(u,v);insert2(N+i+B,1,dfn[u],dfn[v]); } while(bfs())ans-=solve(S,INF); printf("%d ",ans); return 0; }