牛客网暑期ACM多校训练营(第二场) 划水记

原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round2.html

先放榜

再讲故事

  时隔一年,我再次亲眼看见了阿鲁巴行为。上一次应该是 cqz 被阿掉。大概是去年的这个时候。

  中午提前吃饭,第一体验到胖子烧饼的全瘦肉味道。感觉比绍兴的阿中烧饼难吃。

  12:00开始,由于笔记本充电,开场前 20 分钟我只能看题目。

  A 秒掉了,xza 几秒钟后也秒掉了,他去写了。

  D 题 foreverpiano 秒掉了。

  45 分钟,我停止充电,开始切题模式。轮到我写题喽。

  我去看 I 题。想了一会儿 23 行干掉了。一个傻逼错误 wa 了一次。

  听说 H 题挺可做。我和 piano 肛 H 。我表示只会一个非常恶心的树形dp 。

  然后弃疗了。期间 xza 卡一波常数干掉了 G 题。

  J 题貌似挺可做。开始写之后我发现搞错了。

  在又犹豫了半个小时之后,我开始写树套树。写了不到半个小时,又卡了点常数,交一发,1A !用时 3.8s (lim: 4s)

  此时已经 3 个小时了。

  xza 开始肝 H 题了。

  piano 说 B 题过的人多。于是我一起来看 B 题。这才发现我一开始读错题了。

  基环树森林上面跑树形dp 。好像挺不是很难。

  感觉时间不够了,于是我开始写啊写。(xza 已经进入调试代码阶段)

  写了半个小时,写完了。过样例,交一发 wa 掉。

  piano 连续出了两个数据干掉了我。(我的代码又爆了两次罚时)

  4 小时 19 分, xza 干掉 H 题。

  28 分,piano 再出一个数据干掉了我,我补完最后一个傻逼错误之后 A 掉了(总共三个傻逼错误)。 4 小时 31 分。然后弃疗了。

  (为啥没有奖金啊)

最后讲(放)题(代)解(码)

我做了 3 题

I 题

  分四个角落,边的中点,其他地方,三个部分直接随便弄一弄就可以了。

#include <bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,Row[N],Col[N];
int main(){
	scanf("%d%d",&n,&m);
	memset(Row,0,sizeof Row);
	memset(Col,0,sizeof Col);
	while (m--){
		int x,y;
		scanf("%d%d",&x,&y);
		Row[x]=Col[y]=1;
	}
	int ans=4-Col[1]-Col[n]-Row[1]-Row[n];
	if (n&1){
		int x=(n+1)/2;
		ans+=Col[x]&&Row[x]?0:1;
	}
	for (int i=2;i<=n/2;i++)
		ans+=4-(Col[i]+Col[n-i+1]+Row[i]+Row[n-i+1]);
	printf("%d",ans);
	return 0;
}

J 题

  注意一下如果一个点被打了两种不同的标记,就直接死掉了。

  直接树套树实现二维区间修改,单点询问,标记永久化。稍微注意一下常数即可。

%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include <bits/stdc++.h>
#define y1 _0001
using namespace std;
const int N=1000005;
int n,m,Test;
int a[N];
int read(){
	char ch=getchar();
	int x=0;
	while (!isdigit(ch))
		ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
	return x;
}
int T;
struct Segment{
	int lc[N*10],rc[N*10],tag[N*10],tot;
	void init(){
		memset(lc,0,sizeof lc);
		memset(rc,0,sizeof rc);
		memset(tag,0,sizeof tag);
		tot=0;
	}
	void update(int &rt,int L,int R,int xL,int xR){
		if (!rt)
			rt=++tot;
		if (xL==L&&R==xR){
			if (!tag[rt])
				tag[rt]=T;
			else if (tag[rt]!=T)
				tag[rt]=-1;
			return;
		}
		int mid=(L+R)>>1;
		if (xR<=mid)
			update(lc[rt],L,mid,xL,xR);
		else if (xL>mid)
			update(rc[rt],mid+1,R,xL,xR);
		else {
			update(lc[rt],L,mid,xL,mid);
			update(rc[rt],mid+1,R,mid+1,xR);
		}
	}
	int query(int rt,int L,int R,int x){
		if (!rt)
			return 0;
		if (L==R)
			return tag[rt];
		int mid=(L+R)>>1,now;
		if (x<=mid)
			now=query(lc[rt],L,mid,x);
		else
			now=query(rc[rt],mid+1,R,x);
		if (now==tag[rt])
			return now;
		if ((now&&tag[rt])||!~now||!~tag[rt])
			return -1;
		return now?now:tag[rt];
	}
}S;
int root[N<<2];
void update(int rt,int L,int R,int xL,int xR,int yL,int yR){
	if (xL==L&&R==xR){
		S.update(root[rt],1,m,yL,yR);
		return;
	}
	int mid=(L+R)>>1;
	if (xR<=mid)
		update(rt<<1,L,mid,xL,xR,yL,yR);
	else if (xL>mid)
		update(rt<<1|1,mid+1,R,xL,xR,yL,yR);
	else {
		update(rt<<1,L,mid,xL,mid,yL,yR);
		update(rt<<1|1,mid+1,R,mid+1,xR,yL,yR);
	}
}
int query(int rt,int L,int R,int x,int y){
	int tag=S.query(root[rt],1,m,y);
	if (!rt)
		return 0;
	if (L==R)
		return tag;
	int mid=(L+R)>>1,now;
	if (x<=mid)
		now=query(rt<<1,L,mid,x,y);
	else
		now=query(rt<<1|1,mid+1,R,x,y);
	if (now==tag)
		return now;
	if ((now&&tag)||!~now||!~tag)
		return -1;
	return now?now:tag;
}
int main(){
	n=read(),m=read(),Test=read();
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			a[(i-1)*m+j]=read();
	S.init();
	while (Test--){
		int x1=read(),y1=read(),x2=read(),y2=read();
		T=read();
		update(1,1,n,x1,x2,y1,y2);
	}
	int ans=0;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++){
			int now=query(1,1,n,i,j);
			if (now!=0&&now!=a[(i-1)*m+j])
				ans++;
		}
	printf("%d\n",ans);
	return 0;
}

B 题

  显然是基环树森林上面树形dp裸题。

  对于树的情况,直接简单树形dp。对于环上面的,我们选一条边分别让他连或者不连达到断环的目的,然后线性dp一下即可。注意一下二元环的情况(然而我并不用判)(一元环我判了,不知道数据里面有没有)

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=100005;
struct Gragh{
	static const int M=N*2;
	int cnt,y[M],nxt[M],fst[N];
	void clear(){
		cnt=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int n,vis[N],father[N],rvis[N];
int id[N],tot=0;
LL p[N],d[N],dp[N][2];
int flag[N*2];
void DP(int x){
	vis[x]=1;
	dp[x][0]=0;
	for (int i=g.fst[x];i;i=g.nxt[i]){
		int y=g.y[i];
		if (vis[y])
			continue;
		DP(y);
		flag[i]=1;
		dp[x][0]+=dp[y][1];
	}
	dp[x][1]=dp[x][0]+p[x]-d[x];
	for (int i=g.fst[x];i;i=g.nxt[i]){
		int y=g.y[i];
		if (!flag[i])
			continue;
		dp[x][1]=min(dp[x][1],dp[x][0]-dp[y][1]+dp[y][0]+p[y]);
	}
}
LL rdp[N];
LL solve(int x){
	tot=0;
	for (int i=x;!rvis[i];i=father[i])
		id[++tot]=i,rvis[i]=1;
	int L=father[id[tot]],R=tot;
	for (int i=1;i<=tot;i++)
		if (id[i]==L){
			L=i;
			break;
		}
	for (int i=L;i<=R;i++)
		vis[id[i]]=1;
	for (int i=L;i<=R;i++)
		DP(id[i]);
	LL ans=1LL<<50;
	rdp[L-1]=0,rdp[L]=dp[id[L]][1];
	for (int i=L+1;i<=R;i++)
		rdp[i]=min(rdp[i-2]+dp[id[i]][0]+dp[id[i-1]][0]+p[id[i-1]],rdp[i-1]+dp[id[i]][1]);
	ans=min(ans,rdp[R]);
	rdp[L]=dp[id[R]][0]+dp[id[L]][0]+p[id[R]];
	rdp[L+1]=rdp[L]+dp[id[L+1]][1];
	for (int i=L+2;i<R;i++)
		rdp[i]=min(rdp[i-2]+dp[id[i]][0]+dp[id[i-1]][0]+p[id[i-1]],rdp[i-1]+dp[id[i]][1]);
	ans=min(ans,rdp[R-1]);
	if (L==R)
		ans=dp[id[L]][1];
	return ans;
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%lld",&p[i]);
	for (int i=1;i<=n;i++)
		scanf("%lld",&d[i]);
	g.clear();
	for (int i=1,j;i<=n;i++){
		scanf("%d",&j);
		g.add(i,j);
		g.add(j,i);
		father[i]=j;
	}
	memset(vis,0,sizeof vis);
	memset(rvis,0,sizeof rvis);
	memset(dp,0,sizeof dp);
	LL ans=0;
	for (int i=1;i<=n;i++)
		if (!vis[i])
			ans+=solve(i);
	printf("%lld",ans);
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round2.html