• 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
    ",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
    ",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
    ",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
    ",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
    ",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
    考虑最小代价=驻军费用和-不驻军城市费用和
    因此要使不驻军城市费用和最大
    并且不驻军城市是树上的一个独立集
    强制选或不选可以用把权值赋为(pminf)来实现
    于是剩下的就是这道题
    代码如下(略长,见谅)

    #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
    ",all-tmp);
    	}
    }
    

    总结

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

  • 相关阅读:
    mysql 去除重复数据
    linux 相关命令
    mysql load data infile auto increment id
    《Head First 设计模式》读书笔记
    《NoSQL精粹》读书笔记
    linux 服务器丢包故障排查
    《高性能MySQL》 读书总结
    NAT穿透(UDP打洞)
    python函数调用关系图(python call graph)
    VMware 三种网络模式
  • 原文地址:https://www.cnblogs.com/Romeolong/p/10044250.html
Copyright © 2020-2023  润新知