2020牛客NOIP赛前集训营-普及组(第六场)
发布日期:2021-05-07 23:18:49 浏览次数:17 分类:原创文章

本文共 4094 字,大约阅读时间需要 13 分钟。

Powered by:AB_IN 局外人

打表出来二分即可,发现打表到6的时候,就已经超10000了,我其实打多了。

#include<bits/stdc++.h>using namespace std;int a[20],n,ans = 1,b[20];int main(){       cin >> n;    for(int i = 1 ;i <= 10; i++){           a[i] = ans;        ans *= 7;        b[i] = a[i] + b[i-1];    }    int id = lower_bound(b+1, b+11, n) - b;    cout<< id <<endl;}

题意就是要从 1 1 1走到 2 2 2再走到 3 3 3,一直走到 n n n的最短距离,中间有设 m m m个传送点,可以使在任何地方都能到达传送点。

所以 a i a_i ai a i + 1 a_{i+1} ai+1最短路径无非就是两种情况:

  • 第一种:直接过去
  • 第二种:传送至传送门,从传送门直接过去。
#include <bits/stdc++.h>using namespace std;const int N=5e3+10;struct sa{       int x;    int y;}a[N];vector <int> v;int n,m,x,y,c,mn,ans;int main(){       scanf("%d%d",&n,&m);    for(int i = 1; i <= n ;i++){           scanf("%d%d",&a[i].x,&a[i].y);    }    for(int i = 1; i <= m ;i++){           scanf("%d",&c);        v.push_back(c);    }    for(int i = 1; i <= n - 1 ;i++){           if( find(v.begin(),v.end(),i+1) != v.end()) continue;        mn = abs(a[i].x - a[i+1].x) + abs(a[i].y - a[i+1].y);        for(auto j : v){               mn = min(mn , abs(a[j].x - a[i+1].x) + abs(a[j].y - a[i+1].y));        }        ans += mn;    }    printf("%d\n",ans);    return 0;}

想要代码量少,思维量一定要大。
这个题可以用 d f s dfs dfs爆搜,但我太菜了写不出来 ,比赛时写了个 b f s bfs bfs只得了30分。赛后看了眼大佬们的代码,才发现是真的巧妙。

首先核心思想是贪心。反向输入图形,把第 n n n行当作第 1 1 1行,现在的目标就是尽可能的去让球去填满上面的空。但是,有三种情况是会封死路的

  • xx
  • .x
    x.
  • x.
    .x

所以要分区。所以现在问题就转化成了,让球去尽量往上填这一分区的空

先给出代码

#include<bits/stdc++.h>using namespace std;const int N = 3e5+10;int n , r , f , Q[N << 1];long long ans=0;char a[N][5];int main(){       scanf("%d", &n);    for(int i = n; i >= 1;i--) scanf("%s", a[i]);    for(int i = 1; i <= n;i++)    {       	if(a[i][0] != 'x') Q[r++] = i;    	if(a[i][1] != 'x') Q[r++] = i;    	if(a[i][0] == 'o') ans += i - Q[f++];    	if(a[i][1] == 'o') ans += i - Q[f++];    	if((a[i][0]=='x'&&a[i][1]=='x')||(a[i][0]=='x'&&a[i+1][1]=='x')||(a[i+1][0]=='x'&&a[i][1]=='x')) r = f = 0;	}	printf("%lld\n", ans);    return 0;}

一一解释变量。

  • Q Q Q数组下标是递增的,代表目前除了x的个数;
    Q Q Q数组的值代表这个不是x的字符是第几行的。
    顺着下来,上去的球就也相当于点了。

  • r r r就是下标。

    if(a[i][0] != 'x') Q[r++] = i;if(a[i][1] != 'x') Q[r++] = i;
  • f f f为全部空位(包括点和已经上去的球)已被球占用了的个数,也同样作为 Q Q Q的下标。

    if(a[i][0] == 'o') ans += i - Q[f++];if(a[i][1] == 'o') ans += i - Q[f++];

    a n s ans ans加上当前行减去目前存在空位最上面的行 f f f继续自增。

  • 最后,就是当出现三种拦路的情况,直接让 r , f r,f r,f都清 0 0 0即可。相当于就是开辟了个新的分区,啥都清 0 0 0了。

思路很简单,先给每个点跑一遍最短路,记录 a i a_i ai a i + 1 a_{i+1} ai+1的路径长度 l 1 l_1 l1。再去管传送门,从每个传送门这跑一遍最短路,记录传送门到 a i + 1 a_{i+1} ai+1的路径 l 2 l_2 l2 l 1 l_1 l1 l 2 l_2 l2取个最小值即可。

我一开始为了图个方便,把传送门和每个点都建了个边权为 0 0 0的边,导致凭空多出来至少 6 e 5 6e5 6e5条边,就T了,呜呜呜。。。

#include<bits/stdc++.h>using namespace std;#define ll long longconst int N=1e6+10;const int inf=0x3f3f3f3f;struct sa{       int dis;    int pos;};bool operator <(const sa &a,const sa &b) {    return a.dis>b.dis; }priority_queue<sa>q;struct Edge{       int u, v, w, next;}edge[N<<2];int head[N];int cnt;void add_edge(int u, int v, int w){       edge[cnt].u = u;    edge[cnt].v = v;    edge[cnt].w = w;    edge[cnt].next = head[u];    head[u] = cnt++;}int dis[N];bool vis[N];int n,m,s,t,u,v,w,T,k;void dij(int s,int v,int d[]){       memset(vis,0,sizeof(vis));    for (int i=1;i<=n;i++) d[i] = inf;    d[s]=v;    q.push( (sa) {   v,s});	while(!q.empty())	{   		sa ns=q.top();		q.pop();		int x=ns.pos;		if(vis[x]) continue;		vis[x]=1;		for(int i=head[x]; i!=-1 ; i=edge[i].next)		{   			int to=edge[i].v;			int dd=d[x]+edge[i].w;			if(d[to]>dd)			{   				d[to]=dd;				q.push( (sa) {   d[to],to});			}		}	}}int cnt1[N];int main(){       memset(head,-1,sizeof(head));    scanf("%d%d%d",&n,&m,&k);    for(int i = 1 ;i <= m ;i++){           scanf("%d%d%lld",&u,&v,&w);        add_edge(u, v, w);        add_edge(v, u, w);    }    for(int i = 1 ;i <= n - 1 ;i++){           dij(i, 0, dis);        cnt1[i + 1] = dis[i + 1];    }    for(int i = 1 ;i <= k; i++){           scanf("%d",&u);        dij(u , 0 , dis);        for(int j = 1 ;j <= n - 1; j++){              cnt1[j + 1] = min( cnt1[j + 1] , dis[j + 1]);        }    }    long long ans = 0;    for (int i = 2; i <= n; i++) {           ans += cnt1[i];    }    printf("%lld",ans);	return 0;}

完结。

上一篇:爬虫笔记1
下一篇:2020牛客NOIP赛前集训营-普及组(第四、五场)

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2025年04月15日 21时54分16秒