NOIP2018游记&题解

Day 0

openday,open了一天.和yx,zyh三杀
晚上到了学军旁边的酒店.
看了一下电视睡觉了.

Day 1

早上8点到了考场,唯一的感觉是冷.
8点15分进了考场.
700+台笔记本.

密码纪念金庸
Fei2Xue@Lian#Tian!


T1 铺设道路

刚开始看题
woc这个\(T1\)我不会啊
没做过积木大赛啊QAQ
数据结构学傻掉了

于是

秒想一个分治线段树
对于一段\([L,R]\),记录一个\(val\)表示已经铺设的量,然后线段树区间最小找到\(min\)\(min\)的位置,答案加上\(val-min\),然后分治就好了.
时间复杂度\(O(n*log^2n)\)
代码略丑,见谅

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (100010)
#define LL long long 
using namespace std;
struct xds{
	int l,r,pos;
}t[N<<3];
int n,a[N];
LL ans;
int min_(int x,int y){
	if(a[x]<a[y])return x;else return y;
}
void build(int l,int r,int x){
	t[x].l=l,t[x].r=r;
	if(l==r){
		t[x].pos=l;
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,x*2),build(mid+1,r,x*2+1);
	t[x].pos=min_(t[x*2].pos,t[x*2+1].pos);
}
int query(int l,int r,int x){
	if(t[x].l==l&&t[x].r==r)return t[x].pos;
	int mid=(t[x].l+t[x].r)>>1;
	if(r<=mid)return query(l,r,x*2);
	else if(l>mid)return query(l,r,x*2+1);
	else return min_(query(l,mid,x*2),query(mid+1,r,x*2+1));
}
void work(int l,int r,int v){
	if(l>r)return;
	if(l==r){
		ans+=((LL)a[l]-(LL)v);
		return;
	}
	int mi=query(l,r,1),val=a[mi];
	ans+=((LL)val-(LL)v);
	work(l,mi-1,val),work(mi+1,r,val);
}
int main(){
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	build(1,n,1);
	work(1,n,0);
	printf("%lld\n",ans);
}

大概\(9:00\)左右写完了\(T1\),过了大样例,开\(T2\).

T2 货币系统

联赛前模拟赛曾出现过网友这个词语.
对于网友这个词感到莫名慌张QAQ
woc这个\(T2\)我不会做啊
仔细分析(观察大样例),发现答案必定是原来货币系统的子集.
所以直接可行性DP就好了(或者说完全背包)
一发过了大样例,和暴力拍上就不管了
时间复杂度\(O(n*a_{max})\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (110)
#define M (25010)
#define LL long long 
using namespace std;
int n,a[N],m,ans,T;
bool vis[M];
void check(int x){
	for(int i=x;i<=m;i++)
	vis[i]|=vis[i-x];
}
int main(){
	freopen("money.in","r",stdin);
	freopen("money.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n),m=0;
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),m=max(m,a[i]);
		sort(a+1,a+n+1),ans=0;
		memset(vis,0,sizeof(vis)),vis[0]=1;
		for(int i=1;i<=n;i++)
		if(!vis[a[i]])ans++,check(a[i]);
		printf("%d\n",ans);
	}
}

T3 赛道修建

看见最大值最小
果断二分
然后在树上贪心就好了
我用的是set,常数较大,得分95.
洛谷上90,开了O2跑得飞快
时间复杂度\(O(n*log^2n)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#define N (50010)
#define LL long long 
using namespace std;
int n,m,fi[N],ne[N<<1],b[N<<1],c[N<<1],E;
int dis[N],f[N],len[N],a[N],ru[N];
bool used[N];
multiset<int> S;
set<int>::iterator it,it2;
void add(int x,int y,int z){
	ne[++E]=fi[x],fi[x]=E,b[E]=y,c[E]=z,ru[y]++;
}
void dfs(int u,int pre){
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		dis[v]=dis[u]+c[i];
		dfs(v,u);
	}
}
void dp(int u,int pre,int lim){
	int cnt=0;
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		dp(v,u,lim),f[u]+=f[v];
	}
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		if(len[v]+c[i]>=lim)f[u]++;
		else a[++cnt]=len[v]+c[i];
	}
	if(!cnt)return;
	if(cnt==1){
		len[u]=a[1];
		return;
	}
	sort(a+1,a+cnt+1);
	for(int i=1;i<=cnt;i++)used[i]=0;
	//S.clear();
	for(int i=1;i<=cnt;i++)
	S.insert(a[i]);
	for(int i=1;i<=cnt;i++){
		it2=S.find(a[i]);
		if(it2==S.end())continue;
		S.erase(it2);
		it=S.lower_bound(lim-a[i]);
		if(it!=S.end()){
			f[u]++;
			S.erase(it);
		}
		else len[u]=a[i];
	}
}
bool check(int lim){
	memset(f,0,sizeof(f));
	memset(len,0,sizeof(len));
	dp(1,0,lim);
	return f[1]>=m;
}
void dp1(int u,int pre,int lim){
	int cnt=0;
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		dp(v,u,lim),f[u]+=f[v];
	}
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		if(len[v]+c[i]>=lim)f[u]++;
		else a[++cnt]=len[v]+c[i];
	}
	if(!cnt)return;
	if(cnt==1){
		len[u]=a[1];
		return;
	}
	sort(a+1,a+cnt+1);
	for(int i=1;i<=cnt;i++)used[i]=0;
	for(int i=1;i<=cnt;i++){
		if(used[i])continue;
		bool flag=0;
		for(int j=1;j<=cnt;j++){
			if(used[j]||i==j)continue;
			if(a[i]+a[j]>=lim){
				used[i]=used[j]=1,f[u]++,flag=1;
				break;
			}
		}
		if(!flag)len[u]=a[i];
	}
}
bool check1(int lim){
	memset(f,0,sizeof(f));
	memset(len,0,sizeof(len));
	dp1(1,0,lim);
	return f[1]>=m;
}
int main(){
	freopen("track.in","r",stdin);
	freopen("track.out","w",stdout);
	int L=0,R=0; bool flag=1;
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z),R+=z;
	}
	for(int i=1;i<=n;i++)if(ru[i]>5)flag=0;
	if(n<=3000)flag=1;
	dfs(1,0);
	if(m==1){
		int ans=0,ll=1;
		for(int i=2;i<=n;i++)
		if(dis[i]>dis[ll])ll=i;
		dis[ll]=0,dfs(ll,0);
		for(int i=1;i<=n;i++)
		if(dis[i]>ans)ans=dis[i];
		printf("%d\n",ans);
		return 0;
	}
	else if(flag){
		int ans=0;
		while(L<=R){
			int mid=(L+R)>>1;
			if(check1(mid))L=mid+1,ans=mid;
			else R=mid-1;
		}
		printf("%d\n",ans);
	}
	else {
		int ans=0;
		while(L<=R){
			int mid=(L+R)>>1;
			if(check(mid))L=mid+1,ans=mid;
			else R=mid-1;
		}
		printf("%d\n",ans);
	}
}

Day1.5

从考场中出来,大家都\(AK\)了QAQ
我这个\(T3\)菊花图要\(1.5s\)的人感到很慌张
下午打了一会膈膜,休息了一下.
看见个个地方都在喷原题
\(NOIP=POI*N\)
\(T1\)自己抄自己
\(T2,T3=POI\)


Day 2

7:50到了学军.
还是8:15开门
今天的密码是XIAOshushenXIA
特殊字符不记得了.
纪念查大侠


T1 旅行

发现了这是一个\(dfs\)序的问题.
因此先拓扑排序
然后枚举一条边断掉
就好了?
时间复杂度\(O(n^2*logn)\)
环不大,应该速度还行.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (5010)
#define LL long long
#define inf (0x7f7f7f7f)
using namespace std;
int n,m,fi[N],ne[N<<1],b[N<<1],E;
int c[N][N],ind,a[N],ans[N],q[N],ru[N],pp[N],ck[N],cir[N],sum;
bool vis[N];
void add(int x,int y){
	ne[++E]=fi[x],fi[x]=E,b[E]=y,ru[y]++;
}
void dfs(int u,int pre){
	int cnt=0; ans[++ind]=u;
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		c[u][++cnt]=v;
	}
	sort(c[u]+1,c[u]+cnt+1);
	for(int i=1;i<=cnt;i++)
	dfs(c[u][i],u);
}
void dfs1(int u,int pre){
	//cout<<u<<" "<<sum<<endl;
	cir[++sum]=u,vis[u]=1;
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(vis[v]||v==pre)continue;
		dfs1(v,u);
	}
}
void check(int u,int pre,int L,int R){
	int cnt=0; ck[++ind]=u;
	//cout<<u<<" "<<L<<" "<<R<<endl;
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		if(v==L&&u==R||v==R&&u==L)continue;
		c[u][++cnt]=v;
	}
	sort(c[u]+1,c[u]+cnt+1);
	for(int i=1;i<=cnt;i++)
	check(c[u][i],u,L,R);
}
bool min_(){
	for(int i=1;i<=n;i++)
	if(ck[i]<ans[i])return 1;
	else if(ck[i]>ans[i])return 0;
	return 0;
}
void topsort(){
	int h=0,t=0;
	for(int i=1;i<=n;i++)
	if(ru[i]==1)q[++t]=i,vis[i]=1;
	while(h<t){
		int u=q[++h];
		for(int i=fi[u];i;i=ne[i]){
			int v=b[i];
			if(vis[v])continue;
			ru[v]--;
			if(ru[v]<=1&&!vis[v])q[++t]=v,vis[v]=1;
		}
	}
	for(int i=1;i<=n;i++)
	if(!vis[i]){
		dfs1(i,0);
		//cout<<sum<<endl;
		break;
	}
	//for(int i=1;i<=sum;i++)
	//cout<<i<<" "<<cir[i]<<" ";
	//return;
	memset(ans,inf,sizeof(ans));
	for(int i=1;i<sum;i++){
		ind=0; //cout<<cir[i]<<" "<<cir[i+1]<<endl;
		check(1,0,cir[i],cir[i+1]);
		if(min_()){
			for(int j=1;j<=n;j++)ans[j]=ck[j];
		}
	}
	check(1,0,cir[sum],cir[1]);
	if(min_())
	for(int j=1;j<=n;j++)ans[j]=ck[j];
}
int main(){
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	if(m==n-1){
		dfs(1,0);
		for(int i=1;i<=n;i++)printf("%d ",ans[i]);
		return 0;
	}
	topsort();
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
	return 0;
}

然后就去肝\(T2,T3\),然而我并不会其中任意一道.
因此\(rush\)了两个暴力,然后去推\(T2\)了.
推了一个\(65\)分的,结果不知道为什么挂了.
然后\(11:40\)的时候发现\(T3\)暴力挂了.
然后赶紧改了一下.
好像\(inf\)开小了,最后才\(40\)分.


T2 填数游戏

至今还是不会(逃
不贴代码了


T3 保卫王国

机房巨魔txc几天前刚学会动态dp
今天就考到了板子题.
然而并不会动态dp,也不会倍增.
于是考后学习了一个动态dp
考虑最小代价=驻军费用和-不驻军城市费用和
因此要使不驻军城市费用和最大
并且不驻军城市是树上的一个独立集
强制选或不选可以用把权值赋为\(\pm\inf\)来实现
于是剩下的就是这道题
代码如下(略长,见谅)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N (100010)
#define M (200010)
#define inf 1e11
#define rg register int
#define Label puts("NAIVE")
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
inline char read(){
	static const int IN_LEN=1000000;
	static char buf[IN_LEN],*s,*t;
	return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
}
template<class T>
inline void read(T &x){
	static bool iosig;
	static char c;
	for(iosig=false,c=read();!isdigit(c);c=read()){
		if(c=='-')iosig=true;
		if(c==-1)return;
	}
	for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
	if(iosig)x=-x;
}
inline char readchar(){
	static char c;
	for(c=read();!isalpha(c);c=read())
	if(c==-1)return 0;
	return c;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN],*ooh=obuf;
inline void print(char c) {
	if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
	*ooh++=c;
}
template<class T>
inline void print(T x){
	static int buf[30],cnt;
	if(x==0)print('0');
	else{
		if(x<0)print('-'),x=-x;
		for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
		while(cnt)print((char)buf[cnt--]);
	}
}
inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
#define int LL
struct Matrix{
	LL a[2][2];
	Matrix(){memset(a,0,sizeof(a));}
	Matrix operator *(Matrix x){
		Matrix res;
		for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		for(int k=0;k<2;k++)
		res.a[i][j]=max(res.a[i][j],a[i][k]+x.a[k][j]);
		return res;
	}
}a[N<<4],val[N],ans;
int n,m,kkk;
int fi[M],ne[M],b[M],E,ind;
int top[N],fa[N],siz[N],son[N],dfn[N],rdfn[N],ed[N];
LL f[N][2],all,w[N];
void add(int x,int y){
	ne[++E]=fi[x],fi[x]=E,b[E]=y;
}
void dfs1(int u,int pre){
	int maxsiz=-1,ma=0; fa[u]=pre;
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==pre)continue;
		dfs1(v,u);
		if(siz[v]>maxsiz)maxsiz=siz[v],ma=v;
		siz[u]+=siz[v];
	}
	son[u]=ma,siz[u]++;
}
void dfs2(int u){
	dfn[u]=++ind,rdfn[ind]=u;
	if(!son[u]){ed[u]=u;return;}
	top[son[u]]=top[u],dfs2(son[u]),ed[u]=ed[son[u]];
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==son[u]||v==fa[u])continue;
		top[v]=v,dfs2(v);
	}
}
void dp(int u){
	for(int i=fi[u];i;i=ne[i]){
		int v=b[i];
		if(v==fa[u])continue;
		dp(v),f[u][0]+=max(f[v][0],f[v][1]);
		f[u][1]+=f[v][0];
	}
	f[u][1]+=1ll*w[u];
}
void build(int l,int r,int x){
	if(l==r){
		int u=rdfn[l],g0=0,g1=w[u];
		for(int i=fi[u];i;i=ne[i]){
			int v=b[i];
			if(v==fa[u]||v==son[u])continue;
			g0+=max(f[v][0],f[v][1]),g1+=f[v][0];
		}
		a[x].a[0][0]=a[x].a[0][1]=g0;
		a[x].a[1][0]=g1,a[x].a[1][1]=-inf;
		val[l]=a[x];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,x*2),build(mid+1,r,x*2+1);
	a[x]=a[x*2]*a[x*2+1];
}
void change(int k,int l,int r,int x){
	if(l==r){a[x]=val[l];return;}
	int mid=(l+r)>>1;
	if(k<=mid)change(k,l,mid,x*2);
	else change(k,mid+1,r,x*2+1);
	a[x]=a[x*2]*a[x*2+1];
}
Matrix query(int l,int r,int L,int R,int x){
	if(l==L&&r==R)return a[x];
	int mid=(L+R)>>1;
	if(r<=mid)return query(l,r,L,mid,x*2);
	else if(l>mid)return query(l,r,mid+1,R,x*2+1);
	else return query(l,mid,L,mid,x*2)*query(mid+1,r,mid+1,R,x*2+1);
}
void update(int u,LL t){
	int pos=dfn[u];
	val[pos].a[1][0]+=t-w[u],w[u]=t;
	Matrix pre,now;
	while(u){
		pre=query(dfn[top[u]],dfn[ed[u]],1,n,1);
        change(pos,1,n,1);
        now=query(dfn[top[u]],dfn[ed[u]],1,n,1);
        u=fa[top[u]],pos=dfn[u];
        val[pos].a[0][0]+=max(now.a[0][0],now.a[1][0])-max(pre.a[0][0],pre.a[1][0]);
        val[pos].a[0][1]=val[pos].a[0][0];
		val[pos].a[1][0]+=now.a[0][0]-pre.a[0][0];
	}
}
signed main(){
	freopen("defense.in","r",stdin);
	freopen("defense.out","w",stdout);
	read(n),read(m),read(kkk);
	for(int i=1;i<=n;i++)read(w[i]),all+=1ll*w[i];
	for(int i=1,x,y;i<n;i++){
		read(x),read(y);
		add(x,y),add(y,x);
	}
	dfs1(1,0),top[1]=1,dfs2(1),fa[1]=0;
	dp(1),build(1,n,1);
	while(m--){
		int x,y,tmp1,tmp2,op1,op2;
		read(x),read(op1),read(y),read(op2);
		if(fa[x]==y||fa[y]==x){
			if((!op1)&&(!op2)){
				puts("-1");
				continue;
			}
		}
		tmp1=w[x],tmp2=w[y];
		if(op1)update(x,-inf);else update(x,inf);
		if(op2)update(y,-inf);else update(y,inf);
		ans=query(1,dfn[ed[1]],1,n,1);
		LL tmp=max(ans.a[0][0],ans.a[1][0]);
		if(!op1)(tmp-=inf)+=tmp1;
		if(!op2)(tmp-=inf)+=tmp2;
		update(x,tmp1),update(y,tmp2);
		printf("%lld\n",all-tmp);
	}
}

总结

\(100+100+95+100+50+40=485\)
连暴力都没打满
垫底了
技不如人甘拜下风
省选比别人低\(100\)分怎么打啊...
希望学科营不要像\(NOIP\)一样啊..

posted @ 2018-11-30 15:05  Romeolong  阅读(244)  评论(0编辑  收藏  举报