• NOIP2017


    本蒟蒻表示对$NOIP2018$感到很虚。。。

    于是把前几年的$NOIP$做一做。

    第一个当然是我的处女战——$NOIP2017$!

    $Day1$:


    $T1$:小凯的疑惑

    考场上手玩三组数据然后秒出结论的我。。。

    答案就是$ab-a-b$。

    证明?不存在的。。。

    我们设$x=ak+bj(a<b,kin[0,b-1])$。

    显然当$jgeq0$时,$x$有解,舍去。

    于是当$j=-1$时,$x$取到最大值$ak-b(kin[0,b-1])$。

    显然,当$k=b-1$时,$x$取到最大值$a(b-1)-b$。

    于是答案就是$ab-a-b$。

    记得开$long long$。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    long long a,b;
    int main(){
    	cin>>a>>b;
    	printf("%lld
    ",a*b-a-b);
    	return 0;
    }
    

    $T2$:时间复杂度

    考场上暴力写挂的我。。。

    一道巨大的字符转模拟题。

    其实只要按照正常的模拟来做就好。

    注意一些很容易想到的细节。

    我就是因为读数字只读了一位然后暴力写挂的。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define MAXN 10010
    #define MAX 9999999
    using namespace std;
    int n,o;
    struct node{
    	int f,i,x,y,last;
    }a[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    int change(char* x){
    	int date=0,l=strlen(x);
    	for(int i=0;i<l;i++)
    		if(x[i]>='0'&&x[i]<='9')
    			date=date*10+x[i]-'0';
    	return date;
    }
    void work(){
    	int fzd=0,ans=0;
    	for(int i=1;i<=n;i++){
    		if(a[i].f==0){
    			ans=max(fzd,ans);
    			if(a[a[i].last].x<=100&&a[a[i].last].y==MAX)fzd--;
    			continue;
    		}
    		if(a[i].x<=100&&a[i].y==MAX)fzd++;
    		else if((a[i].x==MAX&&a[i].y<=100)||(a[i].x>a[i].y)){
    			int g=1,h=0;
    			while(++i<=n){
    				if(a[i].f==1)g++;
    				else h++;
    				if(g==h&&a[i].f==0)break;
    			}
    		}
    	}
    	if(ans==o)printf("Yes
    ");
    	else printf("No
    ");
    }
    void init(){
    	int b=0,h=0,s[MAXN*10];
    	char d[20];
    	bool flag=false,t[30];
    	memset(a,0,sizeof(a));
    	memset(t,false,sizeof(t));
    	scanf("%d",&n);scanf("%s",d);
    	if(d[2]=='1')o=0;
    	else o=change(d);
    	for(int i=1;i<=n;i++){
    		scanf("%s",d);
    		if(d[0]=='F'){
    			a[i].f=1;b++;
    			scanf("%s",d);a[i].i=d[0]-'a';
    			scanf("%s",d);a[i].x=(d[0]=='n'?MAX:(change(d)));
    			scanf("%s",d);a[i].y=(d[0]=='n'?MAX:(change(d)));
    			if(t[a[i].i])flag=true;
    			t[a[i].i]=true;
    			s[++h]=i;
    		}
    		else{
    			a[i].f=0;b--;
    			int v=s[h];
    			h--;
    			t[a[v].i]=false;
    			a[i].last=v;
    		}
    		if(b<0)flag=true;
    	}
    	if(b!=0||flag){
    		printf("ERR
    ");
    		return;
    	}
    	work();
    }
    int main(){
    	int t=read();
    	while(t--)init();
    	return 0;
    }
    

    $T3$:逛公园

    考场上连暴力都不会写的我。。。

    首先你可以想到,$Day1$的$DP$去哪里了。。。

    于是这题就跟$DP$脱不了干系了(雾。。。

    建议先去做这题:

    P1608 路径统计

    然后就有$30$的部分分了。。。

    然后开始想$DP$:

    首先一个$spfa$不解释。。。

    设$f[u][j]$为到达$u$节点时路径长与最短路径$d$的差为$j$(即:偏移量)。

    于是:

    我们枚举最后总的偏移量,然后记忆化搜索就行了。

    但是这样真的对么?

    你是不是忘了考虑它有的点不能到$n$(巨坑。。。),所以在算偏移量的时候依据从起点开始的最短路是不对的。

    那这怎么办?

    我们把边反过来,从$n$开始最短路,按照这个最短路来算偏移量就行了。

    正确性应该是对的,在下不证了。。。

    那,$0$环怎么办?

    我们只要看$(x,v)$这个状态是否被访问了两次就好了嘛。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #define MAXN 100010
    #define MAXK 55
    #define MAX 999999999
    using namespace std;
    int n,m,c=1,d=1,k,p,ans;
    int ahead[MAXN],bhead[MAXN],path[MAXN],f[MAXN][MAXK];
    bool flag,vis[MAXN],g[MAXN][MAXK];
    struct node{
    	int next,to,w;
    }a[MAXN<<1],b[MAXN<<1];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    void add(int u,int v,int w){
    	a[c].to=v;a[c].w=w;
    	a[c].next=ahead[u];
    	ahead[u]=c++;
    	b[d].to=u;b[d].w=w;
    	b[d].next=bhead[v];
    	bhead[v]=d++;
    }
    inline int relax(int u,int v,int w){
    	if(path[v]>path[u]+w){
    		path[v]=path[u]+w;
    		return 1;
    	}
    	return 0;
    }
    void spfa(){
    	int u,v;
    	queue<int> q;
    	for(int i=1;i<=n;i++){path[i]=MAX;vis[i]=false;}
    	path[n]=0;
    	vis[n]=true;
    	q.push(n);
    	while(!q.empty()){
    		u=q.front();
    		q.pop();
    		vis[u]=false;
    		for(int i=bhead[u];i;i=b[i].next){
    			v=b[i].to;
    			if(relax(u,v,b[i].w)&&!vis[v]){
    				vis[v]=true;
    				q.push(v);
    			}
    		}
    	}
    }
    int dfs(int dis,int x){
    	if(g[x][dis]){
    		flag=true;
    		return 0;
    	}
    	if(f[x][dis]!=-1)return f[x][dis];
    	g[x][dis]=true;
    	int s=0;
    	for(int i=ahead[x];i;i=a[i].next){
    		int v=a[i].to,t=dis-(path[v]+a[i].w-path[x]);
    		if(t>k||t<0)continue;
    		int x=dfs(t,v);
    		s=(s+x)%p;
    		if(flag)return 0;
    	}
    	g[x][dis]=false;
    	if(x==n&&dis==0)s++;
    	return f[x][dis]=s;
    }
    void work(){
    	spfa();
    	for(int i=0;i<=k;i++){
    		memset(g,false,sizeof(g));
    		int x=dfs(i,1);
    		ans=(ans+x)%p;
    	}
    	if(flag)printf("-1
    ");
    	else printf("%d
    ",ans);
    }
    void init(){
    	int u,v,w;
    	c=d=1;
    	ans=0;
    	flag=false;
    	memset(ahead,0,sizeof(ahead));
    	memset(bhead,0,sizeof(bhead));
    	memset(f,-1,sizeof(f));
    	n=read();m=read();k=read();p=read();
    	for(int i=1;i<=m;i++){
    		u=read();v=read();w=read();
    		add(u,v,w);
    	}
    	work();
    }
    int main(){
    	int t=read();
    	while(t--)init();
    	return 0;
    }
    

    总结:

    $Day1$应该按我那时的水平,$200$是肯定有的。

    但是只拿到了$140=100+40+0$很可惜。。。

    被我市的一群初中生吊打。。。

    其实只要暴力打好,应该没有什么问题。

    还有就是,心态要稳。


    $Day2$:

    $T1$:奶酪

    考场上一拿道题——啥啥啥?三维计算几何???我连二维都不会啊。。。

    然后认真审题——毕竟这是$NOIPDay2T1$,出题人是不敢出得太毒的。。。

    赶紧想暴力。。。

    首先发现,如果两个球$(x_1,y_1,z_1),(x_2,y_2,z_2)$有交面,一定满足:

    $$2rgeqsqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}$$

    那个开方可能会有精度误差,于是:

    $$(2r)^2geq (x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2$$

    即:

    $$4r^2geq (x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2$$

    然后我们把所有有交面的球看成一个一个的点,连边。

    再把$z=0,z=h$当成源点和汇点。

    我们发现如果有解,源点与汇点一定存在一条路径。

    然后就可以乱搞了。

    本蒟蒻当时脑子一热,一发$SPFA$上去。。。

    各路大神的做法千奇百怪:并查集、$BFS$、$SPFA$、$Dijkstra$。。。

    然后。。。这个复杂度是$O(n^2)$的吧。。。

    看看我拿了多少分——对于$100\%$的数据,$nleq1000$。。。

    我竟然做完了$T1$!!!

    当时激动得手抖——离省一又近了一大步!

    注意:记得开$long long$。

    但是我当时估测完数据之后发现——极端数据要开$unsigned long long$!

    吓得我赶紧改。。。

    但是出题人并没有卡$long long$。。。

    于是就这样了。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define MAXN 1010
    #define MAX 999999999
    using namespace std;
    int n,h,s,t,c;
    unsigned long long r;
    int head[MAXN],path[MAXN];
    bool vis[MAXN];
    struct node1{
    	long long x,y,z;
    }b[MAXN];
    struct node2{
    	int next,to,w;
    }a[MAXN*MAXN<<1];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    inline int relax(int u,int v,int w){
    	if(path[v]>path[u]+w){
    		path[v]=path[u]+w;
    		return 1;
    	}
    	return 0;
    }
    void add(int x,int y){
    	a[c].to=y;a[c].w=1;
    	a[c].next=head[x];
    	head[x]=c++;
    	a[c].to=x;a[c].w=1;
    	a[c].next=head[y];
    	head[y]=c++;
    }
    bool check(int i,int j){
    	unsigned long long ss=(unsigned long long)(b[i].x-b[j].x)*(b[i].x-b[j].x)+(b[i].y-b[j].y)*(b[i].y-b[j].y)+(b[i].z-b[j].z)*(b[i].z-b[j].z);
    	if(ss>r*r*4)return false;
    	return true;
    }
    bool spfa(){
    	int u,v;
    	queue<int> q;
    	for(int i=0;i<=n+1;i++){path[i]=MAX;vis[i]=false;}
    	path[s]=0;
    	vis[s]=true;
    	q.push(s);
    	while(!q.empty()){
    		u=q.front();
    		q.pop();
    		vis[u]=false;
    		for(int i=head[u];i;i=a[i].next){
    			v=a[i].to;
    			if(relax(u,v,a[i].w)&&!vis[v]){
    				vis[v]=true;
    				q.push(v);
    			}
    		}
    	}
    	if(path[t]==MAX)return false;
    	return true;
    }
    void work(){
    	if(spfa())printf("Yes
    ");
    	else printf("No
    ");
    }
    void init(){
    	bool f=false,g=false;
    	n=read();h=read();r=read();
    	c=1;s=0;t=n+1;
    	memset(head,0,sizeof(head));
    	for(int i=1;i<=n;i++){
    		b[i].x=read();b[i].y=read();b[i].z=read();
    		if(abs(b[i].z)<=r){
    			g=true;
    			add(i,s);
    		}
    		if(abs(b[i].z-h)<=r){
    			f=true;
    			add(i,t);
    		}
    		for(int j=1;j<i;j++)if(check(i,j))add(i,j);
    	}
    	if((!f)||(!g)){
    		printf("No
    ");
    		return;
    	}
    	work();
    }
    int main(){
    	int cases=read();
    	while(cases--)init();
    	return 0;
    }
    

    $T2$:宝藏

    本蒟蒻耗费缀长的一题。。。

    一开始像暴力$DFS$,然后发现并不会写。。。

    前几天学了网络流$Dinic$,里面有个有个$BFS$分层图。

    我一想——好像对于$40\%$的数据是可以的!

    然后开始码码码。。。

    我觉得我现在也不一定能写得出来。。。

    各种写搓+各种写挂。。。

    $1h$后——我$^{TM}$终于过了大样例啊!!!

    于是$40$分就到手了。。。

    ($PS$:后来才发现。写个$DFS$再加个剪枝能拿$70$。。。然而并不会写。。。尴尬)

    正解的话。。。

    我也懒得复制了——戳这里


    $T3$:列队

    考场上二话不说写暴力的我。。。

    其实如果再想一想的话,$50$的平衡树还是能想出来的。。。

    据说正解是树状数组。。。但是我不会啊。。。

    我们观察每一行除了最后一个数之外的数在操作中是如何变化的。

    如果出队在$(x,y)$,那么第$x$行(除了最后一个数)会弹出第$y$个数,它后面的数依次左移,再在最后插入一个数(就是最后一列当前的第x个数)。

    然后,对于最后一列,我们要弹出第$x$个数(插入到第$x$行),再在最后插入一个数(就是刚出队的那个)。

    所以,我们无论是对于行还是列,都要执行两种操作:

    第一,弹出第$k$个数;

    第二,在尾部插入一个数。

    这可以用$Splay$来实现。

    但是这样的话空间复杂度是$O(nm)$。。。

    我们发现有些人从始至终都是左右相邻的,这些人对应的$Splay$节点可以合并。

    于是我们在维护$Splay$的时候,所有连续的数的区间我们用一个节点维护,记录这个节点表示的区间的左右端点;

    如果我们发现要弹出的数在某个节点内部,我们就把它拆开,拆成$3$个节点(其中中间那个节点只有一个数,就是我们要的那个数)。

    拆点的过程可以直接跑$Splay$上的$insert$,删除中间那个节点就是$Splay$上的删除。

    总时间复杂度为$O(nlog_2n)$。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 3000010
    using namespace std;
    long long n,m,q;
    int c=1;
    struct tree{
    	int son[2];
    	int f;
    	long long s,l,r;
    }a[MAXN];
    struct line{
    	int root;
    	line(){root=0;}
    	inline void pushup(int rt){
    		if(!rt)return;
    		a[rt].s=a[rt].r-a[rt].l+1;
    		if(a[rt].son[0])a[rt].s+=a[a[rt].son[0]].s;
    		if(a[rt].son[1])a[rt].s+=a[a[rt].son[1]].s;
    	}
    	inline int newnode(long long l,long long r){
    		int rt=c++;
    		a[rt].f=a[rt].son[0]=a[rt].son[1]=0;
    		a[rt].l=l;a[rt].r=r;a[rt].s=r-l+1;
    		return rt;
    	}
    	inline void turn(int rt,int &f){
    		int x=a[rt].f,y=a[x].f,k=a[x].son[1]==rt;
    		if(x==f)f=rt;
    		else a[y].son[a[y].son[1]==x]=rt;
    		a[rt].f=y;a[x].f=rt;a[a[rt].son[k^1]].f=x;
    		a[x].son[k]=a[rt].son[k^1];a[rt].son[k^1]=x;
    		pushup(x);pushup(rt);
    	}
    	void splay(int rt,int &f){
    		while(rt^f){
    			int x=a[rt].f,y=a[x].f;
    			if(a[rt].f^f)turn((a[x].son[1]==rt)^(a[y].son[1]==x)?rt:x,f);
    			turn(rt,f);
    		}
    		pushup(rt);
    	}
    	int find(int rt){
    		while(a[rt].son[1])rt=a[rt].son[1];
    		return rt;
    	}
    	inline void insert(long long x){
    		int rt=newnode(x,x),last=find(root);
    		a[last].son[1]=rt;a[rt].f=last;
    		splay(rt,root);
    	}
    	inline void buildtree(long long l,long long r){root=newnode(l,r);}
    	int next(int rt){
    		rt=a[rt].son[1];
    		while(a[rt].son[0])rt=a[rt].son[0];
    		return rt;
    	}
    	inline long long split(int rt,long long x){
    		splay(rt,root);
    		x+=a[rt].l-1;
    		int y=newnode(x+1,a[rt].r);
    		a[rt].r=x-1;
    		if(!a[rt].son[1]){
    			a[rt].son[1]=y;
    			a[y].f=rt;
    		}
    		else{
    			int k=next(rt);
    			a[k].son[0]=y;a[y].f=k;
    			splay(y,root);
    		}
    		return x;
    	}
    	long long kth(long long x){
    	    int rt=root;
    		while(1){
    			int y=a[rt].son[0];
    			if(x<=a[y].s)rt=y;
    			else{
    				x-=a[y].s;
    				long long flag=a[rt].r-a[rt].l+1;
    				if(x<=flag){
    					if(x==1){
    						long long ans=a[rt].l;
    						a[rt].l++;
    						splay(rt,root);
    						pushup(rt);
    						return ans;
    					}
    					else if(x==flag){
    						long long ans=a[rt].r;
    						a[rt].r--;
    						splay(rt,root);
    						pushup(rt);
    						return ans;
    					}
    					else return split(rt,x);
    				}
    				x-=a[rt].r-a[rt].l+1;
    				rt=a[rt].son[1];
    			}
    		}
    	}
    }splay[MAXN];
    inline long long read(){
    	long long date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    void init(){
    	n=read();m=read();q=read();
    	for(int i=1;i<=n;i++)splay[i].buildtree(m*(i-1)+1,m*i-1);
    	splay[0].buildtree(m,m);
    	for(int i=2;i<=n;i++)splay[0].insert(m*i);
    }
    void work(){
    	int x,y;
    	long long s;
    	while(q--){
    		x=read();y=read();
    		if(y==m){
    			s=splay[0].kth(x);
    			printf("%lld
    ",s);
    		}
    		else{
    			s=splay[x].kth(y);
    			printf("%lld
    ",s);
    			splay[x].insert(splay[0].kth(x));
    		}
    		splay[0].insert(s);
    	}
    }
    int main(){
        init();
        work();
    	return 0;
    }
    

    总结:

    $Day2$算是拿到了预计的分数:$170=100+40+30$

    但是,怎么说呢,能更好一点吧。

    毕竟有些算法是考完之后听其他大佬一说然后恍然大悟。

    然后又被初中的大佬吊打了。。。


    $NOIP2017$总结:

    其实如果发挥的好的话,应该是$370+$的。

    但是因为码力不足等种种原因,最终只有$310$。。。

    那群初二的巨佬一个个都去了$WC$。。。

    虽然拿到了省一,但是还是有些不足吧。

    最重要的几点:模板记牢;心态平和。

    我就是抱着铁定二等的心态考出了一等。。。

    不管怎么说吧,实力在那里,你能得的分,出题人抢不走;不能的得分,出题人也不会给你。

    $NOIP2018$,加油!

  • 相关阅读:
    Storm-源码分析-Stats (backtype.storm.stats)
    Storm-源码分析-Topology Submit-Task-TopologyContext (backtype.storm.task)
    Storm-源码分析-Streaming Grouping (backtype.storm.daemon.executor)
    Storm-源码分析-Topology Submit-Worker
    Storm-源码分析- Messaging (backtype.storm.messaging)
    Storm-源码分析-LocalState (backtype.storm.utils)
    Storm-源码分析- Disruptor在storm中的使用
    LMAX Disruptor 原理
    Shiro学习(7)与Web整合
    MQTT---HiveMQ源代码具体解释(十四)Persistence-LocalPersistence
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9911101.html
Copyright © 2020-2023  润新知