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
求出两个坐标关于x,y差值的最大公约,然后差值各/最大公约,得到的2个数每+一次,就是从一个点到另一个点移动的过程,而且这个过程必定是在格子上的。
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
using namespace std;
int qq[305000];
int gcd(int x,int y){
if(x%y==0)
return y;
else
return gcd(y,x%y);
}
int walk[300][300];
int main(){
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
int r,c,n,m;
//freopen("in.txt","r",stdin);
int g1,g2,g3;
cin >>n>>m;
memset(walk,0,sizeof(walk));
for(i=1;i<=n;i++){
scanf("%d %d %d %d",&t1,&t2,&t3,&t4);
if(t1==t3){
for(j=min(t2,t4);j<=max(t2,t4);j++)
walk[t1][j]=i;continue;
}else if(t2==t4){
for(j=min(t1,t3);j<=max(t1,t3);j++)
walk[i][t2]=i;continue;
}
f1=t3-t1;f2=t4-t2;
int t5=0;
if(abs(f2)>abs(f1)){
swap(f1,f2);
t5=1;
}
f3=gcd(abs(f1),abs(f2));
if(t5)swap(f1,f2);
g1=f1/f3;g2=f2/f3;
while(t1!=t3||t2!=t4){
walk[t1][t2]=i;
t1+=g1;t2+=g2;
}
walk[t3][t4]=i;
}
cin >> r;
for(i=1;i<=r;i++){
scanf("%d %d",&t1,&t2);
printf("%d\n",walk[t1][t2]);
}
return 0;
}
百度地图上有 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
问最短路,刚开始觉得6W这会超,然后想4w也就是只建立一个点当作城市群看待,可是这样发现这样会导致城市群之间的距离为0了,所以必须得多建立4w个点,当作从一个群的城市到这个群对应的入点的距离为0,这个城市群与各个出城市群是有有向边的,表明这两个城市群可相连,如果1 2 可连,那么需要从如城市群1连到出城市群2,再从如城市群2连到出城市群1,连2次,然后spfa跑一次单源最短路即可。
刚开始一直连样例都看不懂,原来是可以通过城市群从a到b,再从b到a。
#include <iostream>
#include <stdio.h>
#include <queue>
#include <vector>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
struct ttt{
int next;
ll cost;
};
vector<ttt>qq[60500];
ll dist[60500];
int main(){
int i,j,k,f1,f2,f3,f4,t1,t2,t4;
ll t3;
int r,c,n,m;
//freopen("in.txt","r",stdin);
int g1,g2,g3;
cin >>n>>m;
for(i=1;i<=n+m+m;i++)
dist[i]=1e14+7;
ttt u,v;
u.cost=0;
for(i=1;i<=m;i++){
scanf("%d",&t1); //i为第i个城市群
for(j=1;j<=t1;j++){
scanf("%d",&t2);
u.next=n+i;
//cout <<t2 << "连接" << u.next <<endl;
qq[t2].push_back(u);
u.next=t2;
//cout <<i+m+n << "连接" << u.next <<endl;
qq[i+n+m].push_back(u);
}
}
scanf("%d",&k);
for(i=1;i<=k;i++){
scanf("%d %d %lld",&t1,&t2,&t3);
u.cost=t3;
u.next=t2;
qq[t1].push_back(u);
u.next=t1;
qq[t2].push_back(u);}
scanf("%d",&k);
for(i=1;i<=k;i++){
scanf("%d %d %lld",&t1,&t2,&t3);
u.cost=t3;
u.next=n+t2+m;
//cout << n+t1 << "连接~~~" <<u.next <<endl;
qq[n+t1].push_back(u); //左边向右边,单向
u.next=n+t1+m;
//cout << m+t2 << "连接~~~!!" <<u.next <<endl;
qq[n+t2].push_back(u);
}
int s,e;
cin >> s >>e;
dist[s]=0;
queue<int>q1;
q1.push(s);
int u1;
while(!q1.empty()){
u1=q1.front();q1.pop();
for(i=0;i<qq[u1].size();i++){
u=qq[u1][i];
// cout <<u1<<"到" << u.next << endl;
if(dist[u.next]>dist[u1]+u.cost){
dist[u.next]=dist[u1]+u.cost;
q1.push(u.next);
}
}
}
if(dist[e]<1e14+7)
cout <<dist[e] <<endl;
else
cout <<"-1" <<endl;
return 0;
}
腾讯推出了一款益智类游戏——消消乐。游戏一开始,给定一个长度为 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知识不到位
~题目的结果要求是dp[4][1]*1+dp[4][2]*2+dp[4][3]*3这样的形式,就想办法凑成这样的形式
dp[i][j]表示到第i个集合第k步有多少种方法。
显然可以得到当S可以一步去完结的时候,
dp[S|i][k]+=dp[i][k-1]
意思为从i这个集合+一个集合的集合可以由i这个集合在k-1步数只需要多加一步,就可以全部变为S|i集合。
之所以这个步数的转移可以在内部用for,是因为这个状态是由dp[i][k]转移来的,而枚举状态是通过i枚举,那么i的所有可能性都在后面不会改变了的
至于为什么这么循环S|i这个集合中的i都是已经计算好的,这点不是很明白。这里学到了一个关于如何求集合S以外全部集合的方法。
具体做法:
for(i=0;i<(1<<n);i++){
for(u=0;u<n;u++){
if(i>>u&1)continue;
S=0;
nowgcd=q1[u];
for(v=u;v<n;v++){
if(i>>v&1)continue;
S|=1<<v;
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
const ll INF=1e9+7;
ll dp[(1<<18)+5][20];
ll q1[20];
ll gcd1(ll x,ll y){
if(x%y==0)
return y;
else return gcd1(y,x%y);
}
ll gcd(ll x,ll y){
if(y>x)
swap(x,y);
return gcd1(x,y);
}
int main(){
ll i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
ll r,c,n,m;
ll l1,l2,l3;
//freopen("in.txt","r",stdin);
cin >> n>> m;
for(i=0;i<n;i++){ //状压,二进制之类从0开始写
cin >> q1[i];
//dp[(1<<i)][1]=1;
}
dp[0][0]=1;
ll u,v,S,nowgcd;
t1=0;
for(i=0;i<(1<<n);i++){
for(u=0;u<n;u++){
if(i>>u&1)continue;
S=0;
nowgcd=q1[u];
for(v=u;v<n;v++){ //这两个for的意思是,从u为起点,找到全部无i的集合
if(i>>v&1)continue;
nowgcd=gcd(nowgcd,q1[v]); //这里nowgcd是公约以后的结果
if(nowgcd<m)break;
S|=1<<v; //把所有满足的v全部放进来
for(k=1;k<=n;k++){//因为每次都是从i开始向上加,那么就不用把k的循环放到最外层
//这样子是可以很大程度增加速度
dp[S|i][k]+=dp[i][k-1];
dp[S|i][k]%=INF;
/*if((S|i)==3){
cout <<(S|i) << "由" <<S<<" ++++ "<<i <<"组成" <<endl;
cout << dp[S|i][k] << "~ " <<k <<"层"<<endl;
cout << dp[i][k-1] <<" " <<i <<" "<<k-1<<endl;
}*/
//这里是S|i的这个集合,S是可以一次性拿掉的,
//那么S|i的拿走的次数就为dp[i][k-1]步加上拿走S这一步组成
// 因为i是枚举的,那么必定不会漏/重复
}
}
}
}
ll sum1=0;
for(i=1;i<=n;i++){
sum1+=dp[(1<<n)-1][i]*i;
sum1%=INF;
}
cout << sum1<<endl;
return 0;
}