NO.1
题目描述:有一个n*n的矩阵,每个点上有一个值,要求两个矩阵,在只有一个交点的情况下,两个矩阵的值相等,求有多少种方案
思路:枚举+hash+前缀合
矩阵只有以上两种情况,先将**前缀和**f[i,j]求出来,f[i,j]=f[i-1,j]+f[i,j-1]-f[i-1,j-1]+a[i,j]
那么,就可以枚举它们的交点,然后分两种情况枚举:
①枚举第一个矩阵的左上角和第二个矩阵的右下角
每次枚举到第一个矩阵的和x,将这个x存进哈希表里,hash[x]++
枚举到第二个矩阵的和y,就是ans+=hash[y]
②枚举第一个矩阵的右上角和第二个矩阵的左下角
每次枚举到第一个矩阵的和x,将这个x存进哈希表里,hash[x]++
枚举到第二个矩阵的和y,就是ans+=hash[y]
代码:
var a,s:array[0..100,0..100] of longint;
b:array[-2500000..2500000] of longint;
c:array[1..100000] of longint;
n,i,j,k,u,x,l:longint;
ans:qword;
begin
assign(input,'farmer.in');
assign(output,'farmer.out');
reset(input);
rewrite(output);
readln(n);
fillchar(s,sizeof(s),0);
for i:=1 to n do for j:=1 to n do begin read(a[i,j]); s[i,j]:=s[i-1,j]+s[i,j-1]-s[i-1,j-1]+a[i,j]; end;
ans:=0;
fillchar(b,sizeof(b),0);
for i:=1 to n do
for j:=1 to n do
begin
l:=0;
for k:=1 to i do
for u:=1 to j do
begin
x:=s[i,j]-s[i,u-1]-s[k-1,j]+s[k-1,u-1];
inc(b[x]);
inc(l);
c[l]:=x;
end;
for k:=i+1 to n do
for u:=j+1 to n do
begin
x:=s[k,u]-s[k,j]-s[i,u]+s[i,j];
inc(ans,b[x]);
end;
for k:=1 to l do b[c[k]]:=0;
l:=0;
for k:=1 to i do
for u:=j to n do
begin
x:=s[i,u]-s[k-1,u]-s[i,j-1]+s[k-1,j-1];
inc(b[x]);
inc(l);
c[l]:=x;
end;
for k:=i+1 to n do
for u:=1 to j-1 do
begin
x:=s[k,j-1]-s[k,u-1]-s[i,j-1]+s[i,u-1];
inc(ans,b[x]);
end;
for k:=1 to l do b[c[k]]:=0;
end;
writeln(ans);
close(input);
close(output);
end.
NO.2
题目描述:机器人有 M 种,每种机器人能完成 K 个马种之间的语言翻译。问,利用这些机器人,能否实现 1 种群和 N 种群的马语翻译。 若可以,找到翻译过程至少需要用到多少种语言。
思路:SPFA
其实这就是一道最短路,但是如果直接连边就会爆掉
那么,如何实现两边呢?
我们可以将机器人压成一个点,那么问题就转为翻译语言1的机器看做起点,能够翻译语言n的机器看做终点,求起点到终点的最短路。
现在还剩一个问题,就是如果做到连边。
我们开一个数组f[x]表示“语言x下连接的第一台机器”的数组下标,再开数组value[y]与next[y],value[y]中,y就是前面所说数组下标,value[y]就是这一个下标所存的机器的编号,next[y]表示这一个机器所连接的下一个机器的数组下标,当这个机器向下没有连其他机器时,next[y]就等于0。每当有机器a能翻译语言b时,在语言b的后面添加机器a
如果我们要枚举语言b下所有的机器,该怎么做呢?
我们可以将语言b下的所有机器保存下来,然后分别两两连边。
代码:
var n,k,m,x,l,l1,i,j,u,v,i1,head,tail,ans:longint;
g:array[1..1000,1..1000] of longint;
f:array[1..100000] of longint;
value,next:array[1..1000000] of longint;
p,d,first,last,vist:array[1..1000] of integer;
q:array[1..10000000] of longint;
begin
assign(input,'trans.in');
assign(output,'trans.out');
reset(input);
rewrite(output);
readln(n,k,m);
fillchar(g,sizeof(g),0);
fillchar(f,sizeof(f),0);
fillchar(first,sizeof(first),0);
fillchar(last,sizeof(last),0);
fillchar(vist,sizeof(vist),0);
l:=0; head:=1; tail:=0;
for i:=1 to m do
begin
for j:=1 to k do
begin
read(x);
inc(l);
value[l]:=i;
next[l]:=f[x];
f[x]:=l;
if x=1 then first[i]:=1;
if x=n then last[i]:=1;
end;
if first[i]=1 then
begin
inc(tail);
q[tail]:=i;
d[i]:=1;
vist[i]:=1;
end
else d[i]:=10000;
end;
for i:=1 to n do
begin
l1:=0;
j:=f[i];
while j<>0 do
begin
v:=value[j];
inc(l1);
p[l1]:=v;
j:=next[j];
end;
for j:=1 to l1-1 do
for i1:=j+1 to l1 do
begin
g[p[j],p[i1]]:=1;
g[p[i1],p[j]]:=1;
end;
end;
while head<=tail do
begin
u:=q[head];
inc(head);
vist[u]:=0;
for v:=1 to m do
if (g[u][v]=1)and(d[v]>d[u]+1) then
begin
d[v]:=d[u]+1;
if vist[v]=0 then
begin
vist[v]:=1;
inc(tail);
q[tail]:=v;
end;
end;
end;
ans:=10000;
for i:=1 to m do if (last[i]=1)and(d[i]<ans) then ans:=d[i];
if n=1 then writeln(1)
else if ans<10000 then writeln(ans+1)
else writeln(-1);
close(input);
close(output);
end.
NO.3
题目描述:首先,所有乡镇都要求,本乡镇所有的马匹都必须参赛,或者都不参赛(若组队的马匹数量不是该镇马匹数量的约数,将无法参赛)。其次,在本乡镇,选出最佳球队,参加乡镇间联赛。
现在,比赛组织方希望满足所有参赛乡镇的要求,并且使得决赛的马匹尽可能多,请你设计每个球队马匹的数量,使得决赛马匹数最大。注意,决赛至少有 2 个队伍晋级。
80%思路:数学
组队马匹数c1的枚举是必要的,那么接下来我们必须在O(1)的时间内统计出有多少数能够被c1整除,如何做到O(1)呢?只有一个办法,在枚举c1之前,将答案预处理出来,比如我们用一个数组c来保存,其中c[x]表示以x为组队马匹数时,有多少个数能够整除x。
但是c数组该怎么计算呢?枚举x吗?
我们可以对于每个a(i) 求出它的所有因子x,对于每个x,都执行c[x]:=c[x]+1的操作,就可以将c(x)计算出来了。
我们在事先先打一个质数表,然后利用质数表,将a(i)进行质因数分解,再利用a(i)的质因数,算出a(i)的所有因子
计算完c数组后,题目就很容易解决了,枚举组队马匹数c1,求一个最大的c1*c[c1] (c[c1]>1),即为所求答案。
80%代码:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<memory.h>
using namespace std;
bool b[2000];
long ans,n,l,l1,a[200000],c[2000000],d[2000],e[2000],f[2000];
void find(int x,int y)
{
if (x>l1)
{
c[y]++;
return;
}
for (int i=1;i<=f[x];i++)
{
find(x+1,y);
y=y*e[x];
}
find(x+1,y);
}
int main()
{
freopen("polo.in","r",stdin);
freopen("polo.out","w",stdout);
scanf("%ld",&n);
memset(b,true,sizeof(b));
for (int i=1;i<=n;i++) scanf("%ld",&a[i]);
b[1]=false;
for (int i=2;i<=trunc(sqrt(2000)+0.00001);i++)
if (b[i]) for (int j=2;j<=2000/i;j++) b[i*j]=false;
l=0;
for (int i=2;i<=2000;i++)
if (b[i])
{
l++;
d[l]=i;
}
memset(c,0,sizeof(c));
for (int i=1;i<=n;i++)
{
l1=0;
for (int j=1;j<=l;j++)
if (a[i]%d[j]==0)
{
l1++;
e[l1]=d[j];
f[l1]=0;
while (a[i]%d[j]==0)
{
a[i]=a[i]/d[j];
f[l1]++;
}
}
if (a[i]>1)
{
l1++;
e[l1]=a[i];
f[l1]=1;
}
find(1,1);
}
ans=2;
for (int i=1;i<=2000000;i++) if (ans<c[i]*i&&c[i]>1) ans=c[i]*i;
printf("%ld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
100%思路:数学+递推
首先设f[x]表示以x为组队马匹数时,有多少个数能够整除x。
设j为i的因数那么递推公式为:
①f[j]=f[j]+f[i] (如果能整除i必能整除j)
②f[i/j]=f[i/j]+f[i](与上同理)
100%代码:
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
int n;
long long a[2000001],f[2000001],maxn,ans;
void init()
{
maxn=0;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
f[a[i]]++;
if (a[i]>maxn) maxn=a[i];
}
if (maxn==2000000) maxn=maxn/10;
}
void doit()
{
for (int i=1;i<=maxn;i++)
{
if (f[i]==0) continue;
for (int j=1;j*j<=i;j++)
{
if (i%j==0)
{
if (i!=j) f[j]=f[j]+f[i];
if (j!=1&&j*j!=i) f[i/j]=f[i/j]+f[i];
}
}
}
}
int main()
{
freopen("polo.in","r",stdin);
freopen("polo.out","w",stdout);
init();
doit();
ans=0;
for (int i=1;i<=2000000;i++) if (f[i]>1&&f[i]*i>ans) ans=f[i]*i;
printf("%lld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
NO.4
题目描述:给定一个长度为N的数列,求一段连续的子数列满足该子数列中的各元素的平均数大于A,输出可行方案的总数。
思路:树状数组
我们在读入每个元素时,将其减去A,那么问题转换为求一段连续的子数列的平均值大于1
可以用树状数组来维护
有两个操作:①在树状数组枚举到当前的值累加起来,这个值就是操作①对答案的贡献
②改变一个位置的值,对当前的后面的a[2^k]…..加1
代码:
var
a,sum,tree:array[0..100000] of longint;
b:array[0..100000,1..2] of longint;
i,j,k,l,t,n,m:longint;
ans:int64;
procedure qsort(i,j:longint);
var
l,r,mid,mid2:longint;
begin
l:=i;
r:=j;
mid:=b[(i+j) div 2,1];
mid2:=b[(i+j) div 2,2];
repeat
while (b[i,1]<mid)or(b[i,1]=mid)and(b[i,2]>mid2) do inc(i);
while (b[j,1]>mid)or(b[j,1]=mid)and(b[j,2]<mid2) do dec(j);
if i<=j then
begin
b[0]:=b[i];
b[i]:=b[j];
b[j]:=b[0];
inc(i);
dec(j);
end;
until i>j;
if i<r then qsort(i,r);
if l<j then qsort(l,j);
end;
function lowbit(x:longint):longint;
begin
exit(x and -x);
end;
function get(x:longint):longint;
begin
get:=0;
while x>0 do
begin
get:=get+tree[x];
x:=x-lowbit(x);
end;
end;
procedure change(x,y:longint);
begin
while x<=n do
begin
tree[x]:=tree[x]+y;
x:=x+lowbit(x);
end;
end;
begin
assign(input,'sequence.in');
reset(input);
assign(output,'sequence.out');
rewrite(output);
readln(n,m);
for i:=1 to n do
begin
read(a[i]);
a[i]:=a[i]-m;
sum[i]:=sum[i-1]+a[i];
b[i,1]:=sum[i];
b[i,2]:=i;
end;
qsort(1,n);
for i:=1 to n do
begin
ans:=ans+get(b[i,2]-1);
if b[i,1]>0 then inc(ans);
change(b[i,2],1);
end;
writeln(ans);
end.