2017.7.13 C组总结

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.

转载于:https://www.cnblogs.com/Comfortable/p/8412267.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值