洛谷——Day2

本文介绍了两道算法竞赛题目,包括“T1入阵曲”和“T2将军令”的背景故事、题目描述及解题思路。针对“T1入阵曲”,文章提供了计算矩阵子矩形数字和为特定倍数的数量的解决方案;对于“T2将军令”,则讨论了如何在给定条件下使用最少小队控制所有驿站的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

T1 入阵曲

背景

pdf题面和大样例链接:http://pan.baidu.com/s/1cawM7c 密码:xgxv

丹青千秋酿,一醉解愁肠。
无悔少年枉,只愿壮志狂。
题目描述

小 F 很喜欢数学,但是到了高中以后数学总是考不好。

有一天,他在数学课上发起了呆;他想起了过去的一年。一年前,当他初识算法竞赛的 时候,觉得整个世界都焕然一新。这世界上怎么会有这么多奇妙的东西?曾经自己觉得难以 解决的问题,被一个又一个算法轻松解决。

小 F 当时暗自觉得,与自己的幼稚相比起来,还有好多要学习的呢。

一年过去了,想想都还有点恍惚。

他至今还能记得,某天晚上听着入阵曲,激动地睡不着觉,写题写到鸡鸣时分都兴奋不 已。也许,这就是热血吧。

也就是在那个时候,小 F 学会了矩阵乘法。让两个矩阵乘几次就能算出斐波那契数列的 第 10^{100}10
100
项,真是奇妙无比呢。

不过,小 F 现在可不想手算矩阵乘法——他觉得好麻烦。取而代之的,是一个简单的小 问题。他写写画画,画出了一个 n \times mn×m 的矩阵,每个格子里都有一个不超过 kk 的正整数。

小 F 想问问你,这个矩阵里有多少个不同的子矩形中的数字之和是 kk 的倍数? 如果把一个子矩形用它的左上角和右下角描述为 (x_1,y_1,x_2,y_2)(x 1,y 1,x2,y 2 ),其中x_1 \le x_2,y_1 \le y_2x 1≤x2,y 1≤y 2;

输入输出格式

输入格式:
从标准输入中读入数据。

输入第一行,包含三个正整数 n,m,kn,m,k。

输入接下来 nn 行,每行包含 mm 个正整数,第 ii 行第 jj 列表示矩阵中第 ii 行第 jj 列 中所填的正整数 a_{i,j}a
i,j
​ 。

输出格式:
输出到标准输出中。

输入一行一个非负整数,表示你的答案。

输入输出样例

输入样例#1:
2 3 2
1 2 1
2 1 2
输出样例#1:
6
说明

【样例 1 说明】

这些矩形是符合要求的: (1, 1, 1, 3),(1, 1, 2, 2),(1, 2, 1, 2),(1, 2, 2, 3),(2, 1, 2, 1),(2, 3, 2, 3)。
题解:
神奇式子:
sum[i][j]表示前i行,前j列总和
(sum[j][k]-sum[i][k])%modd=0时,sum[j][k]%modd=sum[i][k]%modd
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
long long n,m,modd,a[505][505],sum[505][505];
long long count[1000005],f[10000],ans=0;
inline void read(long long &x)
{
    x=0;int f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    x*=f;
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&modd);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        scanf("%lld",&a[i][j]);
        sum[i][j]=(sum[i-1][j]+a[i][j]+sum[i][j-1]-sum[i-1][j-1])%modd;
    }
    for(int i=0;i<n;i++)//枚举行 
    for(int j=i+1;j<=n;j++)
    {    
         count[0]=1;
        for(int k=1;k<=m;k++)
        {
            f[k]=(sum[j][k]-sum[i][k])%modd;
            if(f[k]<0) f[k]=f[k]+modd;
            ans=ans+count[f[k]];
            count[f[k]]++;
        }
        for(int k=1;k<=m;k++)
         count[f[k]]=0;
    }
    printf("%lld",ans);
}

T2 将军令

题目背景

pdf题面和大样例链接:http://pan.baidu.com/s/1cawM7c 密码:xgxv

历史/落在/赢家/之手
至少/我们/拥有/传说
谁说/败者/无法/不朽
拳头/只能/让人/低头
念头/却能/让人/抬头
抬头/去看/去爱/去追
你心中的梦
题目描述

又想起了四月。

如果不是省选,大家大概不会这么轻易地分道扬镳吧? 只见一个又一个昔日的队友离开了机房。

凭君莫话封侯事,一将功成万骨枯。

梦里,小 F 成了一个给将军送密信的信使。

现在,有两封关乎国家生死的密信需要送到前线大将军帐下,路途凶险,时间紧迫。小 F 不因为自己的祸福而避趋之,勇敢地承担了这个任务。

不过,小 F 实在是太粗心了,他一不小心把两封密信中的一封给弄掉了。

小 F 偷偷打开了剩下的那封密信。他 发现一副十分详细的地图,以及几句批文——原来 这是战场周围的情报地图。他仔细看后发现,在这张地图上标记了 n 个从 1 到 n 标号的 驿站,n − 1 条长度为 1 里的小道,每条小道双向连接两个不同的驿站,并且驿站之间可以 通过小道两两可达。

小 F 仔细辨认着上面的批注,突然明白了丢失的信的内容了。原来,每个驿站都可以驻 扎一个小队,每个小队可以控制距离不超过 k 里的驿站。如果有驿站没被控制,就容易产 生危险——因此这种情况应该完全避免。而那封丢失的密信里,就装着朝廷数学重臣留下的 精妙的排布方案,也就是用了最少的小队来控制所有驿站。

小 F 知道,如果能计算出最优方案的话,也许他就能够将功赎过,免于死罪。他找到了 你,你能帮帮他吗? 当然,小 F 在等待你的支援的过程中,也许已经从图上观察出了一些可能会比较有用的 性质,他会通过一种特殊的方式告诉你。

输入输出格式

输入格式:
从标准输入中读入数据。

输入第 1 行一个正整数 n,k,t,代表驿站数,一支小队能够控制的最远距离,以及特 殊性质所代表的编号。关于特殊性质请参照数据范围。

输入第 2 行至第 n 行,每行两个正整数 u_iu
i
​ ,v_iv
i
​ ,表示在 u_iu
i
​ 和 v_iv
i
​ 间,有一条长度为 一里的小道。

输出格式:
输出到标准输出中。

输出一行,为最优方案下需要的小队数。

输入输出样例

输入样例#1: 复制
4 1 0
1 2
1 3
1 4
输出样例#1: 复制
1

输入样例#2: 复制
6 1 0
1 2
1 3
1 4
4 5
4 6
输出样例#2: 复制
2
说明

【样例 1 说明】

如图。由于一号节点到周围的点距离均是 1,因此可以控制所有驿站。
题解:
需要提前建一个树,计算深度,从叶子节点向上找祖先,再回朔
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
    int num;
    int deep;
}a[400005];
int s=0,vis[400005],n,k,t,u[400005],v[400005],ans=0;
int first[400005],fa[400005],nextt[400005],tot[400005];
int cmp(const node a,const node b)
{
    if(a.deep>b.deep) return 1;
    return 0;
}
void build(int x)//建树 
{ int y;
    for(int i=first[x];i>0;i=nextt[i])
    {
        y=tot[i];
        if(fa[x]!=y)
        { 
            fa[y]=x;
            a[y].num=y;
            a[y].deep=a[x].deep+1;
            build(y);
        }
    }
}
void add(int x,int y)
{
    tot[++s]=y;
    nextt[s]=first[x];
    first[x]=s;
}
int find(int x,int k)
{ if(k==0)
   return x;
  else {
    //vis[fa[x]]=1;
    find(fa[x],k-1);
  }
}
int dfs(int x,int fa,int k)
{  int y; 
    vis[x]=1;
    if(k==0) return 0;
    for(int i=first[x];i>0;i=nextt[i])
    {
        y=tot[i];
        if(y!=fa)
        dfs(y,x,k-1);
    }
}
int main()
{
    scanf("%d%d%d",&n,&k,&t);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&u[i],&v[i]);
        add(u[i],v[i]);
        add(v[i],u[i]);
    }
    a[1].deep=1;a[1].num=1;
    fa[1]=1;
    build(1);
    sort(a+1,a+1+n,cmp); 
    for(int i=1;i<=n;i++)
    {int zz;
        if(!vis[a[i].num])
        {   //vis[a[i].num]=1;
            zz=find(a[i].num,k);
            ans++;
            dfs(zz,0,k);
        }
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值