17.10.6B组总结
T1
完美签到题,先将问题转化为1~r中的数减去1~l-1中的数,再根据容斥原理,用递归,偶数加,奇数减,完美AC……
var
n,l,r,i,j,ll,rr,len,ans:longint;
a,f:array[0..20]of int64;
function gcd(x,y:int64):int64;
begin
if x mod y=0 then
exit(y);
exit(gcd(y,x mod y));
end;
function dg(t,sum,sf:longint;h:int64):longint;
begin
if t>n then
begin
if sum>0 then
inc(f[sum],sf div h);
exit;
end;
dg(t+1,sum,sf,h);
dg(t+1,sum+1,sf,h*a[t] div gcd(h,a[t]));
end;
function find(ss:int64):int64;
var
i,rr,len,s:longint;
begin
fillchar(f,sizeof(f),0);
find:=0;
dg(1,0,ss,1);
for i:=1 to n do
if i mod 2=1 then
inc(find,f[i])
else
dec(find,f[i]);
exit(ss-find);
end;
begin
readln(n,l,r);
for i:=1 to n do
read(a[i]);
writeln(find(r)-find(l-1));
end.
这题居然在今天做提高组15年初赛题目里出现了,无语O__O “…
T2
这是今天比赛里最难的题了,开始想到了50分的状压DP,无奈调不出来,只好弃疗;
正解也很简单,就直接设f[i,j]表示做到了第i行,二进制状态为j,再预处理出该状态之前的状态,轻松搞定50分。
接着发生了神奇的一幕,M在后50分中居然缩小了,于是问题就变成了两部分。我们发现其实对于一个状态i,转移到下一个状态j之后,转移的方法不会改变,于是很自然的想到了矩阵乘法。
多设一个a数组,来将每一个预处理出的前状态与现状态塞到a里面。再用快速幂搞一搞就好了,记得n很大,要用高精度除,最好压位。
大致代码:
const
mo=1000000007;
e=1000000000000000;
type
arr=array[0..32]of int64;
ju=array[0..32,0..32]of int64;
var
n,m,i,j,k,tot:longint;
s,tn,tm:ansistring;
tmp:string;
f:array[0..100,0..10000]of int64;
jl:array[0..15000,1..2]of int64;
a,ans:ju;
g:arr;
procedure dg(x:longint;last,now:int64);
begin
if x>m then
exit;
if x=m then
begin
inc(tot);
jl[tot,1]:=last;
jl[tot,2]:=now;
end;
dg(x+1,(last<<1)+1,now<<1);
dg(x+1,last<<1,(now<<1)+1);
dg(x+2,(last<<2)+3,(now<<2)+3);
end;
function did(x,y:ju):ju;
var
i,j,k:longint;
begin
fillchar(did,sizeof(did),0);
for i:=0 to 1<<m do
for j:=0 to 1<<m do
for k:=0 to 1<<m do
did[i,j]:=(did[i,j]+x[i,k]*y[k,j]) mod mo;
end;
procedure divv(var x:arr);
var
i,tt:longint;
begin
tt:=0;
for i:=x[0] downto 1 do
begin
inc(x[i],tt*e);
tt:=x[i] mod 2;
x[i]:=x[i] div 2;
if x[i]=0 then
x[0]:=i-1;
end;
end;
procedure quick(r:arr);
begin
fillchar(ans,sizeof(ans),0);
for i:=0 to 1<<m do
ans[i,i]:=1;
while r[0]>0 do
begin
if r[1] and 1=1 then
ans:=did(ans,a);
a:=did(a,a);
divv(r);
end;
end;
begin
readln(s);
tn:=copy(s,1,pos(' ',s)-1);
tm:=copy(s,pos(' ',s)+1,length(s)-pos(' ',s));
val(tm,m);
dg(0,0,0);
if (length(tn)<=2)or(tn='100') then
begin
val(tn,n);
if n*m mod 2=1 then
begin
writeln(0);
halt;
end;
f[0,(1<<m)-1]:=1;
for i:=1 to n do
for j:=1 to tot do
f[i,jl[j,2]]:=(f[i,jl[j,2]]+f[i-1,jl[j,1]]) mod mo;
writeln(f[n,(1<<m)-1]);
halt;
end;
for i:=1 to tot do
a[jl[i,1],jl[i,2]]:=1;
fillchar(g,sizeof(g),0);
tmp:='';
i:=length(tn);
j:=0;
while i>=1 do
begin
tmp:=tn[i]+tmp;
inc(j);
if j=15 then
begin
inc(g[0]);
val(tmp,g[g[0]]);
j:=0;
tmp:='';
end;
dec(i);
end;
if j>0 then
begin
inc(g[0]);
val(tmp,g[g[0]]);
end;
quick(g);
m:=(1<<m)-1;
writeln(ans[m,m] mod mo);
end.
T3
暴力都能过,纪中数据水呀!
正解是双向队列+栈维护,可我比赛只得了70,莫名其妙。
对了,c++有个特别好用的自带翻转函数,比正解还快。
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int q[5000010];
int c,k,top;
int main()
{
scanf("%d",&c);
scanf("%d",&k);
while(k)
{
if(k==1)
{
int e;
scanf("%d",&e);
q[++top]=e;
}
if(k==2)
{
if(top<1)
printf("Error: the stack is empty!\n");
else
printf("%d\n",q[top]),top--;
}
if (k==3)
{
if(top<c)
printf("Error: less than %d elements in the stack!\n",c);
else
reverse(q+1+top-c,q+1+top);
}
scanf("%d",&k);
}
return 0;
}
T4
比赛打了十个表懵逼,后来蒙蒙地打了个递推上去,a掉了,听刘犇讲好像是什么奇怪的东西,懵逼 *2。
推一波式子后,发现:
f[i]=(∑j=1j<if[j]+i)÷(i−1)
答案f[n],显而易见。
记得f[1]=0;