细胞分裂
【问题描述】
小A 养了一大坨细胞。
最初小A 只有1 个细胞。每秒,小A 的每个细胞都会分裂成2 个细胞。
已知:现在离“最初”已经过去了x 秒,那么现在的细胞数当然是可以计算的。
小A 想知道的当然不是当前的细胞数。小A 知道他养的细胞的习性:每y
个细胞会聚成一团。经常会有剩下的细胞,那么我们称这些细胞是孤独的。
小A 想知道的就是孤独的细胞个数。
【输入文件】
输入文件为cell.in。
输入文件共一行,为两个整数x y,以空格隔开。
【输出文件】
输出文件为cell.out。
输出文件共一行,为一个整数,即孤独的细胞个数。
【输入样例】
3 3
【输出样例】
2
【数据规模和约定】
对于10%的数据,x<2^6。
对于20%的数据,x<2^17。
对于40%的数据,x<2^64。
对于70%的数据,x<2^2333。
对于100%的收,0≤x<2^233333,y 是3 到1000 之间(含两端)的质数。
数论。
答案 ans = 2^x % y
但是要用费马小定理加速。
PS 据说有人写了100+高精度 EXM??
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 80005;
char s[maxn];
int y,mod;
int main()
{
freopen("cell.in","r",stdin);
freopen("cell.out","w",stdout);
scanf("%s%d",s,&y); mod = y-1;
int p = 0;
int lens = strlen(s);
for(int i=0;i<lens;i++) p=(p*10+s[i]-'0')%mod;
int ans = 1;
for(int i=1;i<=p;i++) (ans<<=1)%=y;
printf("%d",ans);
return 0;
}
通风管
【问题描述】
你是一个Air Conditioning Machinery 公司(ACM)的技术人员(就是装空调的)。不幸的是,当你到一个客户那里去装空调管道的时候,你发现你的管道不够了。你只剩6 条管道,他们都是同一型号的弯管。
你必须在指定的空间内装一个管道。空间是一个长方体,所有的边的都是单位长度的整倍数,可以想象为一个空间堆满了单位正方体。每个弯管占用恰好4个单位的正方体,如下图1 所示。两个弯管不能重合在同一个单位正方体上。每个弯管只有2 个口,它们在图形1 中以灰色显示。你可以把2 个弯管接成一根长的管子,但是它们不得超过给定的空间。图2 表示了其中一种对接方式。你的任务是接通入口和出口。入口和出口在给定空间的外表面上,和单位正方体对齐,如图3 所示。为了减少开支,你必须用最少的弯管解决这个问题。
【输入文件】
输入文件为tube.in。
输入包含一行,为11 个用空格隔开的值。
前3 个是整数(xmax,ymax,zmax)表示给定长方体的长宽高。长方体内的每个单位正方体用坐标(x,y,z)表示, 其中1≤x≤xmax, 1≤y≤ymax, 1≤z≤zmax。
xmax,ymax,zmax 均为正且不大于20。
接下来3 个整数,表示入口所在单位立方体的坐标。
接下来是2 个字符构成的字符串,表示进入的朝向。可能为以下的一种:+x,-x,+y,-y,+z,-z。举例来说,如果为+y,代表进入的方向为y 轴正方向,所以入口面向y 轴负方向。
接下来3 个整数,表示出口所在单位立方体的坐标。
最后是2 个字符构成的字符串,表示流出的朝向。可能为以下的一种:+x,-x,+y,-y,+z,-z。举例来说,如果为+y,代表出去的方向为y 轴正方向,所以出口面向y 轴正方向。(注意与上面的不同之处。)
【输出文件】
输出文件为tube.out。
输出一行,为接通管道,并且不超过指定空间,最少需要的弯管数。如果不
可能用6 个弯管完成,则输出Impossible。
【输入样例1】
5 4 3 3 1 1 +z 5 4 3 +x
【输出样例1】
2
【输入样例2】
5 4 3 3 1 1 +z 1 2 3 -x
【输出样例2】
Impossible
【数据规模和约定】
10 个测试点的答案分别为:Impossible,1,1,2,2,3,3,4,5,6。
注意每个状态都有八个形态可以转移,由于不同方向上转移后没有相同的部分,只好打表。。
其实直接搜索也行。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int dx[][10] =
{
{2,2,2,2,3,3,3,3},
{-2,-2,-2,-2,-3,-3,-3,-3},
{2,0,-2,0,1,0,-1,0},
{2,0,-2,0,1,0,-1,0},
{2,0,-2,0,1,0,-1,0},
{2,0,-2,0,1,0,-1,0}
};
const int dy[][10] =
{
{0,2,0,-2,0,1,0,-1},
{0,-2,0,2,0,-1,0,1},
{2,2,2,2,3,3,3,3},
{-2,-2,-2,-2,-3,-3,-3,-3},
{0,2,0,-2,0,1,0,-1},
{0,-2,0,2,0,-1,0,1}
};
const int dz[][10] =
{
{-2,0,2,0,-1,0,1,0},
{-2,0,2,0,-1,0,1,0},
{0,-2,0,2,0,-1,0,1},
{0,2,0,-2,0,1,0,-1},
{2,2,2,2,3,3,3,3},
{-2,-2,-2,-2,-3,-3,-3,-3}
};
const int dd[][10] =
{
{5,2,4,3,5,2,4,3},
{5,3,4,2,5,3,4,2},
{0,5,1,4,0,5,1,4},
{0,4,1,5,0,4,1,5},
{0,2,1,3,0,2,1,3},
{0,3,1,2,0,3,1,2}
};
const int maxn = 25;
int X,Y,Z;
inline bool inRange(int x,int y,int z) { return 1<=x&&x<=X && 1<=y&&y<=Y && 1<=z&&z<=Z; }
inline bool visible(char ch) { return ch=='+' || ch=='-' || ch=='x' || ch=='y' || ch=='z'; }
struct Node
{
int x,y,z;
int d;
Node () {}
Node (const int &_x,const int &_y,const int &_z,const int &_d) { x=_x; y=_y; z=_z; d=_d; }
inline void read(bool adjust)
{
scanf("%d%d%d",&x,&y,&z);
char op = (char) getchar();
while(!visible(op)) op = (char) getchar();
char ch = (char) getchar();
while(!visible(ch)) ch = (char) getchar();
if(op=='+')
switch(ch)
{
case 'x': d = 0; adjust?x--:0; break;
case 'y': d = 2; adjust?y--:0; break;
case 'z': d = 4; adjust?z--:0; break;
}
else
switch(ch)
{
case 'x': d = 1; adjust?x++:0; break;
case 'y': d = 3; adjust?y++:0; break;
case 'z': d = 5; adjust?z++:0; break;
}
}
}S,T;
queue <Node> que;
int dis[maxn][maxn][maxn][maxn];
bool inque[maxn][maxn][maxn][maxn];
int spfa()
{
memset(dis,0x3f,sizeof(dis));
memset(inque,0,sizeof(inque));
que.push(S); inque[S.x][S.y][S.z][S.d] = true;
dis[S.x][S.y][S.z][S.d] = 0;
while(!que.empty())
{
Node now = que.front(); que.pop(); inque[now.x][now.y][now.z][now.d] = false;
for(int k=0;k<8;k++)
{
int x = now.x + dx[now.d][k];
int y = now.y + dy[now.d][k];
int z = now.z + dz[now.d][k];
int d = dd[now.d][k];
if(!inRange(x,y,z)) continue;
if(dis[x][y][z][d] > dis[now.x][now.y][now.z][now.d] + 1)
{
dis[x][y][z][d] = dis[now.x][now.y][now.z][now.d] + 1;
if(inque[x][y][z][d]) continue;
inque[x][y][z][d] = true;
que.push(Node(x,y,z,d));
}
}
}
return dis[T.x][T.y][T.z][T.d];
}
inline void init()
{
scanf("%d%d%d",&X,&Y,&Z);
S.read(true);
T.read(false);
}
int main()
{
freopen("tube.in","r",stdin);
freopen("tube.out","w",stdout);
init();
int ans = spfa();
if(ans > 6) puts("Impossible");
else printf("%d",ans);
return 0;
}
压路机
【问题描述】
Johnny 开着一辆蒸汽压路机(拖拉机?),像其他的蒸汽压路机一样,它很慢,而且要花更多的时间启动,改变方向,或是停下。Johnny 刚刚完成了一天的工作并正在开着他的蒸汽压路机回家去见他妻子。你的任务是找到对他和他的蒸汽压路机而言的最短路。
Johnny 所住的城市是规则结构的(街道形成了正交系统)。城市街道编排在一个矩网格的节点间。每个节点和它的邻居(最多四个)由街道相连。每个街道恰好1 单位长。当Johnny 进入一个街道,他必须到达另一端,从那里,他可以选择向四个方向行进到下一个节点,依次类推。
在研究了街道的路况之后,Johnny 计算了走过每一条街道的用时。这个时间对两个方向来说是相同的。然而,Johnny 的计算只满足最理想的状态——即蒸汽压路机在进入这条街道时已经进入状态而且不需要加速或刹车。只要蒸汽压路机在经过某条街道之前或之后立即改变方向,它经过这条街道的实际耗时会变为估计值的2 倍。这种情况对于压路机从某个节点发动(比如Johnny 的起点)和到某个节点刹车(比如Johnny 的终点)也适用。
下面的图片是一个例子。数字说明了穿过对应街道的“理想”时间。没有表明数字的街道不允许压路机穿过。Johnny 想要从左上角到右下角。
全部是9 的那条路看起来更加快捷。然而,因为加速和刹车的限制,每一条边都需要2 倍的时间,这使得总时间达到108。由10 组成的路径更快,因为Johnny可以在其中的两条路径上全速通过,即总用时100。
【输入文件】
输入文件为roller.in。
第一行为六个正整数开头:R C r1 c1 r2 c2。R 和C 描述了城市的大小,r1和c1 给出了起点坐标,r2 和c2 给出了Johnny 的家的位置。这两个坐标必定不同。
下一行,有C-1 个非负整数用来描述(1,1)到(1,2),(1,2)到(1,3),(1,3)到(1,4)……的期望耗时。再下一行,有C 个非负整数描述(1,1)到(2,1),(1,2)到(2,2)……的期望耗时。在下一行,又是C-1 个数描述下一行的期望耗时。输入一直这样延续直到描述全部街道。这些整数都表示的是不加速、刹车、转向时的耗时。如果这些特殊情况中的一种或多种发生,那么时间翻倍。这些整数中可能会有0,表示这条路不能经过。
【输出文件】
输出文件为roller.out。
输出一个整数,即最短总耗时。如果不能到达,输出Impossible。
【输入样例】
4 4 1 1 4 4
10 10 10
9 0 0 10
0 0 0
9 0 0 10
9 0 0
0 9 0 10
0 9 9
【输出样例】
100
【数据规模和约定】
对于50%的数据,R,C≤20。
对于100%的数据,输入中所有数不会超过100。
据说是蓝书上面的原题。。
用dp[x][y][d][opt]表示状态,直接建图spfa即可。。貌似Dijkstra更快但是我不会。。
每次转移的时候可以从全速状态转移到减速状态,但是还要加上原来的cost,用3-direction快速查询。。
最后的答案还可能在全速状态中,那么就需要加上最后的这个cost。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 105;
const int dx[] = {-1,0,0,1};
const int dy[] = {0,-1,1,0};
int cost[maxn][maxn][4];
int X,Y;
int Sx,Sy,Tx,Ty;
inline void init()
{
scanf("%d%d%d%d%d%d",&X,&Y,&Sx,&Sy,&Tx,&Ty);
memset(cost,0x3f,sizeof(cost));
for(int k=1;k<(X<<1);k++)
if(k&1)
for(int i=1;i<Y;i++)
{
int tmp;
scanf("%d",&tmp);
if(!tmp) continue;
cost[(k+1)>>1][i][2] = tmp;
cost[(k+1)>>1][i+1][1] = tmp;
}
else
for(int i=1;i<=Y;i++)
{
int tmp;
scanf("%d",&tmp);
if(!tmp) continue;
cost[k>>1][i][3] = tmp;
cost[(k>>1)+1][i][0] = tmp;
}
}
inline bool inRange(int x,int y) { return 1<=x&&x<=X && 1<=y&&y<=Y; }
struct Node
{
int x,y,d,opt;
Node (const int &_x,const int &_y,const int &_d,const int &_opt) { x=_x; y=_y; d=_d; opt=_opt; }
};
queue <Node> que;
int dis[maxn][maxn][4][2]; // 0 for full speed , 1 for blocked
bool inque[maxn][maxn][4][2];
int spfa()
{
memset(dis,0x3f,sizeof(dis));
memset(inque,0,sizeof(inque));
for(int k=0;k<4;k++)
{
int nowx = Sx + dx[k];
int nowy = Sy + dy[k];
if(!inRange(nowx,nowy) || cost[Sx][Sy][k]>=INF) continue;
dis[nowx][nowy][k][1] = cost[Sx][Sy][k] << 1;
que.push(Node(nowx,nowy,k,1));
inque[nowx][nowy][k][1] = true;
}
while(!que.empty())
{
Node now = que.front(); que.pop();
inque[now.x][now.y][now.d][now.opt] = false;
for(int k=0;k<4;k++)
{
int x = now.x + dx[k];
int y = now.y + dy[k];
if(!inRange(x,y) || cost[now.x][now.y][k]>=INF) continue;
int opt = !!(now.d^k);
int val = dis[now.x][now.y][now.d][now.opt];
if(opt)
{
val += cost[now.x][now.y][k] << 1;
if(!now.opt) val += cost[now.x][now.y][3-now.d]; // same value as the opposite direction
}
else val += cost[now.x][now.y][k];
if(dis[x][y][k][opt] > val)
{
dis[x][y][k][opt] = val;
if(inque[x][y][k][opt]) continue;
inque[x][y][k][opt] = true;
que.push(Node(x,y,k,opt));
}
}
}
int ans = INF;
for(int k=0;k<4;k++) smin(ans,min(dis[Tx][Ty][k][1],dis[Tx][Ty][k][0]+cost[Tx][Ty][3-k]));
return ans;
}
int main()
{
freopen("roller.in","r",stdin);
freopen("roller.out","w",stdout);
init();
int ans = spfa();
if(ans >= INF) puts("Impossible");
else printf("%d\n",ans);
return 0;
}