2017计蒜客复赛

40 篇文章 0 订阅
37 篇文章 0 订阅

复赛拿衣服,,,

 B. Windows 画图

在 Windows 的“画图”工具里,可以绘制各种各样的图案。可以把画图当做一个标准的二维平面,在其上先后绘制了 nn 条颜色互不相同的线段。

按绘制的时间顺序,从先到后把线段依次编号为 11 到 nn。第 ii 条线段的两个端点分别为 (xa_i,ya_i)(xai,yai) 和 (xb_i,yb_i)(xbi,ybi),线段的粗细忽略不计。后绘制的线段不会改变之前绘制的线段的位置。

请写一个程序,回答 qq 组询问,每组询问给出一个坐标 (x_i,y_i)(xi,yi),你需要算出在这个点上最后绘制的线段编号。

输入格式

第一行包含两个正整数 n,m(1\leq n\leq 80000,1\leq m\leq 250)n,m(1n80000,1m250),分别表示线段的数目以及坐标的最大取值(下面会具体说明)。

接下来 nn 行,每行输入四个正整数 xa_i,ya_i,xb_i,yb_ixai,yai,xbi,ybi (1\leq xa_i,ya_i,xb_i,yb_i\leq m,(1xai,yai,xbi,ybim, (xa_i,ya_i)\neq(xb_i,yb_i))(xai,yai)(xbi,ybi)),依次表示每条线段两个端点的坐标。

接下来一行,输入一个正整数 q(1\leq q\leq 62500)q(1q62500),表示询问的组数。

接下来 qq 行,每行输入两个正整数 x_i,y_i(1\leq x_i,y_i\leq m)xi,yi(1xi,yim),分别表示每组询问的坐标。

输出格式

输出 qq 行,每行一个整数,表示该位置最上面(最后绘制)的线段的编号。

若该点上不存在线段,请输出 00

样例解释

样例对应题目描述中的图。

样例输入
5 8
2 5 5 2
5 2 3 8
8 4 1 4
2 2 5 8
8 7 4 1
4
3 4
5 2
6 4
3 5
样例输出
4
2
5
0

因为m比较小,所以把每条线的每个点都跑一遍,坑爹卡精度,wa了半天。。。


#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define eps 1e-15
using namespace std;
struct node{
    int x1,y1,x2,y2;
}s[101010];
int vis[350][350];
int main()
{
    int n,m;
  while(~scanf("%d%d",&n,&m))
  {
      memset(vis,0,sizeof(vis));
      for(int i=1;i<=n;i++)
      {
          scanf("%d%d%d%d",&s[i].x1,&s[i].y1,&s[i].x2,&s[i].y2);
          if(s[i].x1>s[i].x2||(s[i].x1==s[i].x2&&s[i].y1>s[i].y2))
          {
              int t=s[i].x1;s[i].x1=s[i].x2;s[i].x2=t;
              t=s[i].y1;s[i].y1=s[i].y2;s[i].y2=t;
          }
          int x1=s[i].x1,x2=s[i].x2,y1=s[i].y1,y2=s[i].y2;
          if(x1!=x2)
          {
              int b=y2-y1,d=x2-x1;
              for(int j=x1;j<=x2;j++)
              {
                  int a=b*(j-x1);
                  if(a%d==0)
                  {
                      int c=a/d;
                      c+=y1;
                      vis[j][c]=i;
                  }
              }
          }
          else
          {
              for(int j=y1;j<=y2;j++)
              {
                  vis[s[i].x1][j]=i;
              }
              continue;

          }
      }
      int q;
      scanf("%d",&q);
      for(int j=0;j<q;j++)
      {
          int x,y;
          scanf("%d%d",&x,&y);
          printf("%d\n",vis[x][y]);
      }
  }
}


 D. 百度地图导航

百度地图上有 nn 个城市,城市编号依次为 11 到 nn。地图中有若干个城市群,编号依次为 11 到 mm。每个城市群包含一个或多个城市;每个城市可能属于多个城市群,也可能不属于任何城市群。

地图中有两类道路。第一类道路是 城市之间的快速路,两个城市 u,vu,v 之间增加一条距离为 cc 的边;第二类道路是 城市群之间的高速路,连接两个城市群 a,ba,b,通过这条高速路,城市群 aa 里的每个城市与城市群 bb 里的每个城市之间两两增加一条距离为 cc 的边。图中所有边均为无向边。

你需要计算从城市 ss 到城市 tt 的最短路。

输入格式

第一行输入 n(1 \le n \le 20000),n(1n20000), m(0 \le m \le 20000)m(0m20000),分别表示城市总数和城市群总数。

接下来一共输入 mm 行。

第 ii 行首先输入一个 k_i(1 \le k_i \le n)ki(1kin),表示第 ii 个城市群中的城市数为 k_iki。接下来输入 k_iki 个数,表示第 ii 个城市群中每个城市的编号(保证一个城市群内的城市编号不重复且合法,\sum_{i=1}^{m}k_i \le 20000i=1mki20000)。

下一行输入一个整数 m_1(0 \le m_1 \le 20000)m1(0m120000),表示有 m_1m1 条第一类道路,即 城市之间的快速路

接下来 m_1m1 行,每行输入三个整数 u_i,v_i(1 \le u_i, v_i \le n),c_i(1 \le c_i \le 10^6)ui,vi(1ui,vin),ci(1ci106),分别表示快速路连接的两个城市编号和边的距离。

下一行输入一个整数 m_2(0 \le m_2 \le 20000)m2(0m220000),表示有 m_2m2 条第二类道路,即 城市群之间的高速路

接下来 m_2m2 行,每行输入三个整数 a_i,b_i(1 \le a_i, b_i \le m),l_i(1 \le l_i \le 10^6)ai,bi(1ai,bim),li(1li106),分别表示快速路连接的两个城市群编号和边的距离。

最后一行输入 s, t(1 \le s, t \le n)s,t(1s,tn),表示起点和终点城市编号。

输出格式

输出一个整数,表示城市 ss 到城市 tt 到最短路。如果不存在路径,则输出-1

样例说明

1 -> 2 - > 5或者1 -> 4 -> 5是最短的路径,总长度为 1212

样例输入
5 4
2 5 1
2 2 4
1 3
2 3 4
2
1 2 9
1 5 18
2
1 2 6
1 3 10
1 5
样例输出
12

主要难在建图,每个城市群建两个点,分别与城市相连,一个只有入边,一个只有出边。城市群之间也同理,分别是入边和出边。

为什么要这么做呢?刚开始把城市群看做一个点,城市与城市群之间,城市群与城市群之间都是双向边,这样有个问题,同一城市群之间的城市距离都为0了。所以要控制不能通过城市群这个节点让群内的城市连通,所以就想到了拆点,单向边。

 dijkstra用了堆优化模板,可以单独拿出来用


#include <iostream>
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll INF=110000000000;
const int maxn=60000+5;
struct Edge{
	int from,to;
	ll weight;
	Edge(int from,int to,ll weight):from(from),to(to),weight(weight){}
};
struct HeapNode{  //prority_queue 中的优先级
	int u;
	ll dist;   //dist: u点到起点的最短路 ,u: 有向边的终点
	HeapNode(int u,int d):u(u),dist(d){}
	bool operator < (const HeapNode& h) const {
		return dist>h.dist;
	}
};
struct Dijkstra{ //打包在Dijkstra中
	int n,m;
	vector<Edge> edges;
	vector<ll> G[maxn];
	bool done[maxn];
	ll dist[maxn];
	ll p[maxn];
	Dijkstra(int n):n(n){
		for(int i=0;i<n;i++) G[i].clear();
		edges.clear();
	}
	void AddEdge(int from,int to,ll weight){
		edges.push_back(Edge(from,to,weight));
		m=edges.size();
		G[from].push_back(m-1);  //保存from出发的边
	}
	void dijkstra(int s)
	{
		priority_queue<HeapNode> Q;
		for(int i=0;i<maxn;i++) dist[i]=INF;
		memset(done,false,sizeof(false));
		dist[s]=0;
		Q.push(HeapNode(s,0));
		while(!Q.empty())
		{
			int u=Q.top().u; Q.pop();
			if(done[u]) continue;
			done[u]=true;
			for(int i=0;i<G[u].size();i++)
			{
				Edge& e=edges[G[u][i]];
				int v=e.to ;
				ll w=e.weight;
				if(dist[v]>dist[u]+w)
				{
					dist[v]=dist[u]+w;
					p[v]=G[u][i];          //记录到各点的最短路径
					Q.push(HeapNode(v,dist[v]));
				}
			}
		}
	}
};


int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        Dijkstra d(n+m+m);
        for(int i=1;i<=m;i++)
        {
            int k;
            scanf("%d",&k);
            for(int j=1;j<=k;j++)
            {
                int x;
                scanf("%d",&x);
                d.AddEdge(x,i+n,0);
                d.AddEdge(i+n+m,x,0);
            }
        }
        int m1;
        scanf("%d",&m1);
        for(int i=1;i<=m1;i++)
        {
            int u,v;
            ll c;
            scanf("%d%d%lld",&u,&v,&c);
            d.AddEdge(u,v,c);
            d.AddEdge(v,u,c);
        }
        int m2;
        scanf("%d",&m2);
        for(int i=1;i<=m2;i++)
        {
            int u,v;
            ll c;
            scanf("%d%d%lld",&u,&v,&c);
            d.AddEdge(u+n,v+n+m,c);
            d.AddEdge(v+n,u+n+m,c);
        }
        int s,t;
        scanf("%d%d",&s,&t);
        d.dijkstra(s);
        if(d.dist[t]>=INF) printf("-1\n");
        else    printf("%lld\n",d.dist[t]);
    }
}


F. 腾讯消消乐

腾讯推出了一款益智类游戏——消消乐。游戏一开始,给定一个长度为 nn 的序列,其中第 ii 个数为 A_iAi

游戏的目标是把这些数全都删去,每次删除的操作为:选取一段连续的区间,不妨记为 [L,R][L,R],如果这一段区间内所有数的最大公约数 \geq kkkk 值在游戏的一开始会给定),那么这一段区间就能被直接删去。

注意:一次删除以后,剩下的数会合并成为一个连续区间。

定义 f(i)f(i) 为进行 ii 次操作将整个序列删完的方案数。

你需要实现一个程序,计算 \sum_{i=1}^{n}{(f(i) \ast i)} \text{ mod } 1000000007i=1n(f(i)i) mod 1000000007

输入格式

第一行输入两个整数 n,k(1\le n \le 18)n,k(1n18)

第二行输入 nn 个正整数 a_i(1 \le a_i \le 10^5)ai(1ai105),表示初始序列中的每个数。

输入数据保证 1 \le k \le \min(a_1,a_2,\ldots a_n)1kmin(a1,a2,an)

输出格式

输出一个整数,表示算出的答案。

样例说明

对于样例 1 而言,f(1)=1f(1)=1f(2)=9f(2)=9f(3)=26f(3)=26f(4)=24f(4)=24

对于样例 2,f(1)=0f(1)=0f(2)=2f(2)=2

样例输入1
4 1
1 1 1 1
样例输出1
193
样例输入2
2 2
2 3
样例输出2
4
样例输入3
1 233
233
样例输出3
1

状压dp强行搞一波,一看这么小的数据量就忍不住硬上

#include <iostream>
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MOD=1000000007;
const int N=(1<<18)+10;
int a[22];
ll d[N][22];

int gcd(int a,int b)
{
    return a%b==0?b:gcd(b,a%b);
}

int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=0;i<n;i++)    scanf("%d",&a[i]);
        int ed=1<<n;
        memset(d,0,sizeof(d));
        d[0][0]=1;
        for(int t=0;t<ed;t++)
        {
            int ssum=0;
            for(int i=0;i<n;i++)    if((t>>i)&1) ssum++;

            for(int c=0;c<=ssum;c++){
            for(int i=0;i<n;i++)
            {
                if((t>>i)&1)    continue;
                int sum=a[i];
                int tp=0;
                for(int j=i;j<n;j++)
                {
                    if((t>>j)&1)    continue;
                    sum=gcd(sum,a[j]);
                    if(sum<k)   break;
                    tp|=(1<<j);
                    d[t|tp][c+1]+=d[t][c];

                }
            }
            }
        }
        ll ans=0;
        for(int i=1;i<=n;i++)   ans=((d[ed-1][i]*i)%MOD+ans)%MOD;
        printf("%lld\n",ans);
    }
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值