- Time flies、、、
- 上午
- BOZJ 1057 [ZJOI2007]棋盘制作
神奇悬线大法好、、、
h[i][j]:(i,j)可以向上延伸多长
转移:h[i][j]=(j!=1&&(mp[i][j]^mp[i-1][j]))?h[i-1][j]+1:1
L[i][j]:(i,j)可以向左延伸多长
转移:L[i][j]=(j!=1&&(mp[i][j]^mp[i][j-1]))?L[i][j-1]+1:1
R[i][j]:(i,j)可以向右延伸多长
转移:R[i][j]=(j!=n&&(mp[i][j]^mp[i][j+1]))?R[i][j+1]+1:1
l[i][j]:以(i,j)为下底上的一点,h(i,j)为高的矩形可以向左延伸多长
转移:l[i][j]=(i!=1&&(mp[i][j]^mp[i-1][j]))?min(L[i][j],l[i-1][j]):L[i][j]
r[i][j]:以(i,j)为下底上的一点,h(i,j)为高的矩形可以向右延伸多长
转移:r[i][j]=(i!=1&&(mp[i][j]^mp[i-1][j]))?min(R[i][j],r[i-1][j]):R[i][j]
然后对于每个位置(i,j)
以它为下底上的一点,h(i,j)为高的矩形面积 = h[i][j]*(r[i][j]+l[i][j]-1)
至于正方形,面积 = min(h[i][j],r[i][j]+l[i][j]-1)*min(h[i][j],r[i][j]+l[i][j]-1)
(转移还是很清晰的,但这个方法真的很666)
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 2005 using namespace std; int mp[MAXN][MAXN]; int h[MAXN][MAXN],l[MAXN][MAXN],r[MAXN][MAXN],L[MAXN][MAXN],R[MAXN][MAXN]; int n,m,ans1,ans2; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) h[i][j]=(j!=1&&(mp[i][j]^mp[i-1][j]))?h[i-1][j]+1:1; for(int j=1;j<=m;j++) L[i][j]=(j!=1&&(mp[i][j]^mp[i][j-1]))?L[i][j-1]+1:1; for(int j=m;j>=1;j--) R[i][j]=(j!=n&&(mp[i][j]^mp[i][j+1]))?R[i][j+1]+1:1; for(int j=1;j<=m;j++){ l[i][j]=(i!=1&&(mp[i][j]^mp[i-1][j]))?min(L[i][j],l[i-1][j]):L[i][j]; r[i][j]=(i!=1&&(mp[i][j]^mp[i-1][j]))?min(R[i][j],r[i-1][j]):R[i][j]; ans1=max(ans1,min(h[i][j],r[i][j]+l[i][j]-1)*min(h[i][j],r[i][j]+l[i][j]-1)); ans2=max(ans2,h[i][j]*(r[i][j]+l[i][j]-1)); } } printf("%d %d",ans1,ans2); return 0; }
- lence选讲
- 下午
- BZOJ 1058 [ZJOI2007]报表统计
stl大法好
第一个可重集维护序列相邻两个元素的差值,
插入元素时,先抹去插入位置的差值,在加入两个新的差值
第二个可重集维护所有的元素,加入一个新元素时就取出比它小的和比它大的那两个元素
,更新答案。
代码:
#include<set> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f #define MAXN 500005 #define siter set<int>::iterator using namespace std; multiset<int>s1,s2; int Begin[MAXN],End[MAXN]; int ans1=INF,ans2=INF,n,m; void Getans2(int x){ siter si=s2.find(x),sj; if(si!=s2.begin()) sj=si,sj--,ans2=min(ans2,x-(*sj)); sj=si; sj++; if(sj!=s2.end()) ans2=min(ans2,*(sj)-x); } int main(){ char s[15]; int a,b,len; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&Begin[i]),End[i]=Begin[i]; for(int i=1;i<=n;i++){ if(i!=n) s1.insert(abs(Begin[i+1]-End[i])); s2.insert(Begin[i]); Getans2(Begin[i]); } for(int i=1;i<=m;i++){ scanf("%s",s); len=strlen(s); if(len==6){ scanf("%d%d",&a,&b); if(a!=n){ siter si=s1.find(abs(Begin[a+1]-End[a])); s1.erase(si); s1.insert(abs(Begin[a+1]-b)); } s1.insert(abs(b-End[a])); End[a]=b; s2.insert(b); Getans2(b); } else if(len==7) { ans1=*(s1.begin()); printf("%d ",ans1); } else if(len==12) printf("%d ",ans2); } return 0; }
- lence又来?!
- 晚上
- BZOJ 1059 [ZJOI2007]矩阵游戏
二分图匹配
对于一个黑点(i,j)
把点i向j+n连边,然后二分图匹配
如果是完美匹配的话,则合法
因为完美匹配选出来的那些点是行列各异的,一定可以调整到主对角线上去。
代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; struct edge{ int to,next; }e[200*200+50]; int vis[405]; int head[405],match[405]; int n,T,ent,cnt,tim; void init(){ memset(head,0,sizeof(head)); memset(match,0,sizeof(match)); ent=2; cnt=0; } void add(int u,int v){ e[ent]=(edge){v,head[u]}; head[u]=ent++; } bool Find(int u){ for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(vis[v]==tim) continue; vis[v]=tim; if(!match[v]||Find(match[v])){ match[v]=u; return 1; } } return 0; } int main(){ scanf("%d",&T); while(T--){ init(); int x; scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=n+1;j<=2*n;j++){ scanf("%d",&x); if(x) add(i,j); } for(int i=1;i<=n;i++){ tim++; if(Find(i)) cnt++; } if(cnt==n) puts("Yes"); else puts("No"); } return 0; }
- BZOJ 1060 [ZJOI2007]时态同步
贪心吧。
最后大家都要达到的时间就是耗时最长的那个叶子的时间,因为增加最终时间的话代价只会更大。
计算出每个叶子需要延长的时间 ,
tim[i]表示i节点与fa[i]的边上预计增加的权值
显然 对于 u节点的所有儿子v
tim[u]=min(tim[v])
上式的意思是u的儿子都需要增加某一权值的话,那就直接加给u和fa[u]那条边上。
然后u的儿子v剩下的权值tim[v]-tim[u]就累计到ans,表示把剩下的权值放在v与u的那条边上。
由于s(起点)无fa,所以特别的tim[u]=0
代码:
#include<cstdio> #include<cstring> #include<iostream> #define ll long long #define INF 0x3f3f3f3f3f3f3f3f #define MAXN 500005 using namespace std; struct edge{ int to,val,next; }e[MAXN*2]; int head[MAXN]; ll tim[MAXN],ans,maxtime; int n,s,ent=2; void read(int &x){ static int f; static char ch; x=0; f=1; ch=getchar(); while(ch<'0'||'9'<ch){if(ch=='-') f=-1;ch=getchar();} while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } void add(int u,int v,int w){ e[ent]=(edge){v,w,head[u]}; head[u]=ent++; } void dfs1(int u,int fa,ll t){ if(!e[head[u]].next) tim[u]=t,maxtime=max(maxtime,t); //叶子节点 for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa) continue; dfs1(v,u,t+e[i].val); } } void dfs2(int u,int fa){ if(!e[head[u]].next) tim[u]=maxtime-tim[u]; //叶子节点 else tim[u]=(u==s)?0:INF; //非叶子节点 for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa) continue; dfs2(v,u); tim[u]=min(tim[u],tim[v]); } for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa) continue; ans+=tim[v]-tim[u]; } } int main(){ read(n); read(s); for(int i=1,a,b,c;i<n;i++) read(a),read(b),read(c),add(a,b,c),add(b,a,c); dfs1(s,0,0); dfs2(s,0); printf("%lld",ans); return 0; }
- BZOJ 1061 [Noi2008]志愿者招募
极好的题!!
让我对网络流建图产生了一点新的感觉
大佬详细题解,写得很好很详细,赞一个:
https://www.byvoid.com/zhs/blog/noi-2008-employee
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define INF 0x3f3f3f3f using namespace std; struct edge{ int to,capo,cost,next; }e[30010]; int head[1010]; int n,m,ent=2,S,T; void add(int u,int v,int capo,int cost){ e[ent]=(edge){v,capo,cost,head[u]}; head[u]=ent++; e[ent]=(edge){u,0,-cost,head[v]}; head[v]=ent++; } void Readin_Build(){ scanf("%d%d",&n,&m); S=n+2,T=n+3; for(int i=1,last,now=0,w;i<=n+1;i++){ last=now; now=0; if(i<=n) scanf("%d",&now); w=now-last; if(w>0) add(S,i,w,0); else add(i,T,-w,0); } for(int i=1,l,r,w;i<=m;i++){ scanf("%d%d%d",&l,&r,&w); add(l,r+1,INF,w); } for(int i=1;i<=n;i++) add(i+1,i,INF,0); } bool spfa(int &Cost){ static bool inq[1010]; static int dis[1010],low[1010],p[1010]; queue<int>q; memset(dis,0x3f,sizeof(dis)); q.push(S); inq[S]=1; dis[S]=0; low[S]=INF; while(!q.empty()){ int u=q.front(); q.pop(); inq[u]=0; for(int i=head[u];i;i=e[i].next){ if(!e[i].capo) continue; int v=e[i].to; if(dis[v]<=dis[u]+e[i].cost) continue; dis[v]=dis[u]+e[i].cost; low[v]=min(e[i].capo,low[u]); p[v]=i; if(!inq[v]) q.push(v),inq[v]=1; } } if(dis[T]==INF) return 0; Cost+=low[T]*dis[T]; int u=T; while(u!=S){ e[p[u]].capo-=low[T]; e[p[u]^1].capo+=low[T]; u=e[p[u]^1].to; } return 1; } void Mincost_Maxflow(){ int Cost=0; while(spfa(Cost)); printf("%d",Cost); } int main(){ Readin_Build(); Mincost_Maxflow(); return 0; }