jzoj C组 2017.1.21
第一题——Sum the Square
题目描述
当你把一个正整数的各个数位平方之后相加,你会发现一些奇妙的现象,它出现了循环。比如说{5, 25, 29, 85, 89, 145, 42, 20, 4, 16, 37, 58, 89, …}。当然,说这个性质纯粹是为了好玩。为了多一些乐趣,这里给出两张图,这是两组平凡的数列:
现在,给定两个整数a1和b1,分别按照上述的方式构造数列{a1, a2, …, am}和{b1, b2, …, bn},其中am等于bn,你的目标是使得n+m最小。
输入
输入包含若干组数据,每组数据包含一行两个整数,分别为a1和b1。
输入0 0表示结束。
输出
每组数据输出一行,包含:a1,空格,b1,空格,n+m。
样例输入
89 89
19 100
61 19
0 0
样例输出
89 89 2
19 100 5
61 19 0
数据范围限制
【数据范围】 1<=A,B<=10^9
测试数据少于2000组
纯模拟,现将a的环求出来,然后再求B的环,如果当前b环的数也还A的环中,就判断该n+m是否比原来的小,则更新值。
代码如下:
var x,y,a,b,lx,ly,min:longint;
fx,fy:array[0..1000]of longint;
function sq(x:longint):longint;
var s:string;
i:longint;
begin
s:='';
str(x,s);
sq:=0;
for i:=1 to length(s) do sq:=sq+sqr(ord(s[i])-48);
end;
begin
assign(input,'square.in');
assign(output,'square.out');
reset(input);
rewrite(output);
while 0=0 do
begin
readln(x,y);
a:=x; b:=y;
min:=maxlongint;
lx:=0;
ly:=0;
fillchar(fx,sizeof(fx),#0);
fillchar(fy,sizeof(fy),#0);
if (x=0)and(y=0) then exit;
if x=y then
begin
writeln(x,' ',y,' ',2);
continue;
end;
if x>729 then
begin
x:=sq(x);
inc(lx);
end;
if y>729 then
begin
y:=sq(y);
inc(ly);
end;
while fx[x]=0 do
begin
inc(lx);
fx[x]:=lx;
x:=sq(x);
end;
while fy[y]=0 do
begin
inc(ly);
if (fx[y]+ly<min)and(fx[y]>0) then min:=fx[y]+ly;
fy[y]:=sq(y);
y:=sq(y);
end;
if min=maxlongint then writeln(a,' ',b,' ',0)
else writeln(a,' ',b,' ',min);
end;
close(input);
close(output);
end.
第二题——TheNumberGame
题目描述
Alice和Bob在玩一个游戏。游戏开始时,他们被给定一个整数。两个人轮流进行操作,每次操作时,玩家可以选取当前整数T的任意一个因子x,不包括1和T。然后将T-x交给下一名玩家。不能操作的玩家输。Alice先手。Alice和Bob都积极的进行游戏。你希望知道对于某些整数,谁会赢得游戏。
输入
数据包括若干组,每组数据包含一行一个整数,表示游戏开始时的整数。
输出
每组数据输出一行,Alice或Bob,表示胜者。
样例输入
2
3
6
样例输出
Bob
Bob
Alice
【样例说明】
当初始整数为2和3时,Alice无法操作,故Bob获胜。
当初始整数为6时,Alice可以选择3,使得Bob无法操作,故Alice胜。
数据范围限制
【数据范围】
对于30%的数据,输入整数<=10^5,数据少于100组
对于100%的数据,1<=输入整数<=10^18,数据少于1000组
找规律:①如果整数为奇数,则Bob赢
②如果整数是2的奇数次方,则Bob赢
③如果都不满足上面的两种情况,则Alice赢
代码如下:
var i:longint;
n:qword;
f:boolean;
a:array[1..32]of qword;
begin
assign(input,'game.in');
assign(output,'game.out');
reset(input);
rewrite(output);
a[1]:=2;
for i:=2 to 32 do a[i]:=a[i-1]*4;
while not eoln do
begin
readln(n);
f:=false;
if n mod 2=1 then
begin
writeln('Bob');
end
else
begin
for i:=1 to 32 do
if a[i]=n then begin writeln('Bob'); f:=true; break; end;
if f=false then writeln('Alice');
end;
end;
close(input);
close(output);
end.
第三题——Mixing Chemicals
题目描述
实验室有n瓶化学药品,编号为0到n-1,你知道第i瓶只有和第c[i]瓶放在一起才会发生爆炸。为了整理实验室,你需要将他们装进k个不同的盒子里。显然,为了你的生命安全,你不能把两瓶会造成爆炸的药品放进同一个箱子。你希望计算出有多少中不同的方案。为了降低难度,你只需要将答案mod 1000000007。
输入
第一行一个整数T,表示有T组测试数据。
对于每组数据
第一行两个整数n,k
第二行n个整数表示c[i]
输出
对于每组数据输出一行一个整数。
样例输入
3
3 3
1 2 0
4 3
1 2 0 0
3 2
1 2 0
样例输出
6
12
0
数据范围限制
【数据范围】
1<=T<=50
1<=n<=100
2<=k<=1000
0<=c i < n,i≠c[i]
对于30%的数据T,n,k<=50
终于将这鬼题AK了(多亏了林大犇)的指导。
我们可以将不能放在一起的瓶子,看成两个点连成一条边(这样就形成了几个环套树也就是连通块),可以填k种颜色。
然后我们求出每个点可以填涂的颜色的数量,设f[i,0/1]表示为第i个点,0为填除黑色外的颜色,1为填黑色。
dp式为 f[i,0]:=(f[i-1,1]* (k-1)+f[i-1,0]*(k-2)) mod mo; f[i,1]:=f[i-1,0] mod mo;
mo为要取摸的数
然后我们将每个点在哪个环求出来,在求出连通块的个数,然后ans:=ans* (f[lt,1]*k mod mo)mod mo; lt为当前循环的搜到的连通块的个数
最后 n:=n-z; for i:=1 to n do ans:=ans*(k-1) mod mo; writeln(ans); 求出将每个连通块的填颜色的个数相乘就求出ans,最后输出就好了。
代码如下:
const mo=1000000007;
var t,i,n,k,w,j:longint;
lt,ans,temp,z:int64;
f:array[0..100,0..1]of int64;
a,b:array[0..100]of longint;
begin
assign(input,'mix.in');
assign(output,'mix.out');
reset(input);
rewrite(output);
readln(t);
for j:=1 to t do
begin
fillchar(a,sizeof(a),#0);
fillchar(b,sizeof(b),#0);
readln(n,k);
f[1,0]:=k-1;
f[0,1]:=1;
for i:=2 to n do
begin
f[i,0]:=(f[i-1,1]*(k-1)+f[i-1,0]*(k-2)) mod mo;
f[i,1]:=f[i-1,0] mod mo;
end;
for i:=0 to n-1 do
begin
read(a[i]);
b[i]:=233;
end;
z:=0;
ans:=1;
readln;
for i:=0 to n-1 do
if b[i]=233 then
begin
temp:=i;
while b[temp]=233 do
begin
b[temp]:=i;
temp:=a[temp];
end;
if b[temp]<>i then continue;
lt:=0;
w:=temp;
repeat
inc(lt);
temp:=a[temp];
until temp=w;
ans:=ans*(f[lt,1]*k mod mo)mod mo;
z:=z+lt;
end;
n:=n-z;
for i:=1 to n do ans:=ans*(k-1) mod mo;
writeln(ans);
end;
close(input);
close(output);
end.
今天做提高模拟题,一大堆0分的人(当然也有我),正是欲哭无泪啊!
感觉以我现在的水平,做两万年都做不出来(没有大神的指导),我还是先回松山湖修炼修炼吧。