17.10.20

    • 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;
}
原文地址:https://www.cnblogs.com/zj75211/p/7701823.html