[luogu模拟赛] 11.2

//0 + 0 + 8
//qaq我太水了

入阵曲(rally)

【题目描述】
丹青千秋酿,一醉解愁肠。
无悔少年枉,只愿壮志狂。
小 F 很喜欢数学,但是到了高中以后数学总是考不好。
有一天,他在数学课上发起了呆;他想起了过去的一年。一年前,当他初识算法竞赛的
时候, 觉得整个世界都焕然一新。 这世界上怎么会有这么多奇妙的东西?曾经自己觉得难以
解决的问题,被一个又一个算法轻松解决。
小 F 当时暗自觉得,与自己的幼稚相比起来,还有好多要学习的呢。
一年过去了,想想都还有点恍惚。
他至今还能记得,某天晚上听着入阵曲,激动地睡不着觉,写题写到鸡鸣时分都兴奋不
已。也许,这就是热血吧。
也就是在那个时候, 小 F 学会了矩阵乘法。 让两个矩阵乘几次就能算出斐波那契数列的
第 10 100 项,真是奇妙无比呢。
不过,小 F 现在可不想手算矩阵乘法——他觉得好麻烦。取而代之的,是一个简单的小
问题。 他写写画画, 画出了一个 ? × ? 的矩阵, 每个格子里都有一个不超过 ? 的正整数。
小 F 想问问你,这个矩阵里有多少个不同的子矩形中的数字之和是 ? 的倍数?
如果把一个子矩形用它的左上角和右下角描述为 (? 1 ,? 1 ,? 2 ,? 2 ),其中? 1 ≤ ? 2 ,? 1 ≤ ? 2 ;
那么,我们认为两个子矩形是不同的,当且仅当他们以 (? 1 ,? 1 ,? 2 ,? 2 ) 表示时不同;也就是
说,只要两个矩形以 (? 1 ,? 1 ,? 2 ,? 2 ) 表示时相同,就认为这两个矩形是同一个矩形,你应该
在你的答案里只算一次。
【输入格式】
从文件 rally.in 中读入数据。
第一行,包含三个正整数 ?,?,?。
输入接下来 ? 行,每行包含 ? 个正整数,第 ? 行第 ? 列表示矩阵中第 ? 行第 ? 列
中所填的正整数 ? ?,? 。
【输出格式】
输出到文件 rally.out 中。
输入一行一个非负整数,表示你的答案。
【样例 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)。
【样例 2】
见选手目录下的 rally/rally2.in 与 rally/rally2.ans 。
【数据范围与约定】
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解
决一部分数据。
每个测试点的数据规模及特点如下表:
n <=400, m <= 400, k <= 1e6

//原本想打55分的前缀和
//结果打的太快错了好几个地方,也没有好好检查,爆了0

55分暴力解法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 400 + 100;
int a[maxn][maxn], sum[maxn][maxn];
int n,m,k,ans = 0;

int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch -'0';
        ch =getchar();
    }
    return x * f;
}

bool pd(int x1, int y1, int x2, int y2) {
    return ( sum[x1][y1] - sum[x1][y2] - sum[x2][y1] + sum[x2][y2] ) % k == 0;
}

int main() {
    n = read(), m = read(), k = read();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            a[i][j] = read();
            sum[i][j] = (sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + a[i][j]) % k;
        }
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            for(int k = i; k <= n; k++) {
                for(int l = j; l <= m; l++) {
                    if(pd(k,l,i - 1,j - 1)) ans++;
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

正解优化了一维
(q - p) % k == 0
q % k == p % k
所以暴力枚举i,j(i,j为x)
再枚举k(k为y)
统计有多少个余数,加起来就好

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 400 + 100;
int a[maxn][maxn], sum[maxn][maxn],cnt[1000010],b[1000010];
int n,m,k;
long long ans = 0;

int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch -'0';
        ch =getchar();
    }
    return x * f;
}

int main() {
    n = read(), m = read(), k = read();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            a[i][j] = read();
            sum[i][j] = (sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + a[i][j]) % k;
        }
    }
    for(int i = 0; i < n; i++) {
        for(int j = i + 1; j <= n; j++) {
			cnt[0] = 1;
            for(int t = 1; t <= m; t++) {
                b[t] = (sum[j][t] - sum[i][t]) % k;
                if(b[t] < 0) b[t] += k;
                ans += cnt[b[t]];
				cnt[b[t]]++;
            }
            for(int t = 1; t <= m; t++) cnt[b[t]] = 0;
        }
    }
    cout<<ans<<endl;
    return 0;
}

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

又想起了四月。

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

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

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

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

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

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

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

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

输入输出格式

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

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

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

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

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

输入输出样例

输入样例#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

n <= 1e5,. k <= 20

//贪心想错了,以为是按照每个点的入度数来做

正解是
按照点的深度排序(BFS序的倒序也可以),爆搜即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 100000 + 100;
int n,k,t;
struct edge {
    int u,v,next;
}e[maxn<<1];
int head[maxn], tot = 0;
int sum = 0, vis[maxn];

void add(int u, int v) {
    e[++tot] = (edge){u,v,head[u]};
    head[u] = tot;
}

int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch -'0';
        ch =getchar();
    }
    return x * f;
}

int fa[maxn],dep[maxn],val[maxn];
void dfs(int x, int f) {
    for(int i = head[x]; i; i = e[i].next) {
        int v = e[i].v;
        if(v != f) {
        	fa[v] = x;
        	dep[v] = dep[x] + 1;
        	val[v] = v;
            dfs(v,x);
        }
    }
}

bool cmp(int a, int b) {
    return dep[a] > dep[b];
}

int ans = 0;

void dfs_(int x,int f,int d) {
    vis[x] = 1;
    if(!d) return;
    for(int i = head[x]; i; i = e[i].next) {
        int v = e[i].v;
        if(v != f) {
            dfs_(v,x,d - 1);
        }
    }
}

int main() {
    //freopen("in.txt","r",stdin);
    n = read(), k = read(), t = read();
    for(int i = 1; i < n; i++) {
        int u = read(), v = read();
        add(u,v),add(v,u);
    }
    if(k == 0) {
        cout<<n<<endl;
        return 0;
    }
    fa[1] = 1;
    dep[1] = 1;
    val[1] = 1;
    dfs(1,1);
    sort(val + 1, val + 1 + n,cmp);
    for(int i = 1; i <= n; i++) {
    	int v = val[i];
        if(!vis[v]) {
    		for(int j = 1; j <= k; j++) v = fa[v];
    		dfs_(v,0,k);sum++;
    	}
    }
    cout<<sum<<endl;
    return 0;
}

//T3随机数8分
正解是状压DP(去年刚考了,今年应该不会考吧,不改了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值