复赛拿衣服,,,
B. Windows 画图
在 Windows 的“画图”工具里,可以绘制各种各样的图案。可以把画图当做一个标准的二维平面,在其上先后绘制了 n 条颜色互不相同的线段。
按绘制的时间顺序,从先到后把线段依次编号为 1 到 n。第 i 条线段的两个端点分别为 (xai,yai) 和 (xbi,ybi),线段的粗细忽略不计。后绘制的线段不会改变之前绘制的线段的位置。
请写一个程序,回答 q 组询问,每组询问给出一个坐标 (xi,yi),你需要算出在这个点上最后绘制的线段编号。
输入格式
第一行包含两个正整数 n,m(1≤n≤80000,1≤m≤250),分别表示线段的数目以及坐标的最大取值(下面会具体说明)。
接下来 n 行,每行输入四个正整数 xai,yai,xbi,ybi (1≤xai,yai,xbi,ybi≤m, (xai,yai)≠(xbi,ybi)),依次表示每条线段两个端点的坐标。
接下来一行,输入一个正整数 q(1≤q≤62500),表示询问的组数。
接下来 q 行,每行输入两个正整数 xi,yi(1≤xi,yi≤m),分别表示每组询问的坐标。
输出格式
输出 q 行,每行一个整数,表示该位置最上面(最后绘制)的线段的编号。
若该点上不存在线段,请输出 0。
样例解释
样例对应题目描述中的图。
样例输入
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. 百度地图导航
百度地图上有 n 个城市,城市编号依次为 1 到 n。地图中有若干个城市群,编号依次为 1 到 m。每个城市群包含一个或多个城市;每个城市可能属于多个城市群,也可能不属于任何城市群。
地图中有两类道路。第一类道路是 城市之间的快速路,两个城市 u,v 之间增加一条距离为 c 的边;第二类道路是 城市群之间的高速路,连接两个城市群 a,b,通过这条高速路,城市群 a 里的每个城市与城市群 b 里的每个城市之间两两增加一条距离为 c 的边。图中所有边均为无向边。
你需要计算从城市 s 到城市 t 的最短路。
输入格式
第一行输入 n(1≤n≤20000), m(0≤m≤20000),分别表示城市总数和城市群总数。
接下来一共输入 m 行。
第 i 行首先输入一个 ki(1≤ki≤n),表示第 i 个城市群中的城市数为 ki。接下来输入 ki 个数,表示第 i 个城市群中每个城市的编号(保证一个城市群内的城市编号不重复且合法,∑i=1mki≤20000)。
下一行输入一个整数 m1(0≤m1≤20000),表示有 m1 条第一类道路,即 城市之间的快速路。
接下来 m1 行,每行输入三个整数 ui,vi(1≤ui,vi≤n),ci(1≤ci≤106),分别表示快速路连接的两个城市编号和边的距离。
下一行输入一个整数 m2(0≤m2≤20000),表示有 m2 条第二类道路,即 城市群之间的高速路。
接下来 m2 行,每行输入三个整数 ai,bi(1≤ai,bi≤m),li(1≤li≤106),分别表示快速路连接的两个城市群编号和边的距离。
最后一行输入 s,t(1≤s,t≤n),表示起点和终点城市编号。
输出格式
输出一个整数,表示城市 s 到城市 t 到最短路。如果不存在路径,则输出-1
。
样例说明
1 -> 2 - > 5
或者1 -> 4 -> 5
是最短的路径,总长度为 12。
样例输入
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. 腾讯消消乐
腾讯推出了一款益智类游戏——消消乐。游戏一开始,给定一个长度为 n 的序列,其中第 i 个数为 Ai。
游戏的目标是把这些数全都删去,每次删除的操作为:选取一段连续的区间,不妨记为 [L,R],如果这一段区间内所有数的最大公约数 ≥k(k 值在游戏的一开始会给定),那么这一段区间就能被直接删去。
注意:一次删除以后,剩下的数会合并成为一个连续区间。
定义 f(i) 为进行 i 次操作将整个序列删完的方案数。
你需要实现一个程序,计算 ∑i=1n(f(i)∗i) mod 1000000007。
输入格式
第一行输入两个整数 n,k(1≤n≤18)。
第二行输入 n 个正整数 ai(1≤ai≤105),表示初始序列中的每个数。
输入数据保证 1≤k≤min(a1,a2,…an)。
输出格式
输出一个整数,表示算出的答案。
样例说明
对于样例 1 而言,f(1)=1,f(2)=9,f(3)=26,f(4)=24。
对于样例 2,f(1)=0,f(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);
}
}