入阵曲(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 。
【数据范围与约定】
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解
决一部分数据。
每个测试点的数据规模及特点如下表:
//55 分 N^4 暴力代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 405
typedef long long LL;
int a[MAXN][MAXN];
LL sum[MAXN][MAXN];
inline void read(int &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(int argc,char *argv[]){
int n,m,k;
read(n),read(m),read(k);
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j){
read(a[i][j]);
sum[i][j] = sum[i-1][j] + a[i][j] + sum[i][j-1] - sum[i-1][j-1];
}
int Ans = 0;
for(int a=1; a<=n; ++a)
for(int b=1; b<=m; ++b)
for(int i=a; i<=n; ++i)
for(int j=b; j<=m; ++j)
if((sum[i][j] - sum[a-1][j] - sum[i][b-1] + sum[a-1][b-1]) % k == 0) ++Ans;
printf("%d\n",Ans);
return 0;
}
//AC 代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 410
typedef long long LL;
int a[MAXN][MAXN],b[MAXN],cnt[1000005];
LL sum[MAXN][MAXN];
inline void read(int &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;
}
LL Ans = 0;
int main(int argc,char *argv[]){
freopen("rally.in","r",stdin);
freopen("rally.out","w",stdout);
int n,m,Mod;
read(n),read(m),read(Mod);
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j){
read(a[i][j]);
sum[i][j] = (sum[i-1][j] + a[i][j] + sum[i][j-1] - sum[i-1][j-1]);
if(sum[i][j] >= Mod) sum[i][j] -= Mod;
}
for(int i=0; i<n; ++i)
for(int j=i+1; j<=n; ++j){
cnt[0] = 1;
for(int k=1; k<=m; ++k) {
b[k] = (sum[j][k] - sum[i][k] ) % Mod;
if(b[k] < 0) b[k] += Mod;
Ans += cnt[b[k]];
++cnt[b[k]];
}
for(int k=1; k<=m; ++k) cnt[b[k]] = 0;
}
printf("%I64d\n",Ans);
fclose(stdin); fclose(stdout);
return 0;
}
建议先看这道题:链接
关于这个AC代码,我得好好说说道道,i、j枚举子矩阵的上下边界,k枚举我们处理到了第几列,把i、j行之间,右边界是第k列的矩阵压成一个数,之后枚举统计,具体原理在链接的那一篇博客说的很详细,N^3可过,还给开O2优化。
将军令(general)
【题目描述】
历史/落在/赢家/之手
至少/我们/拥有/传说
谁说/败者/无法/不朽
拳头/只能/让人/低头
念头/却能/让人/抬头
抬头/去看/去爱/去追
你心中的梦
又想起了四月。
如果不是省选,大家大概不会这么轻易地分道扬镳吧?
只见一个又一个昔日的队友离开了机房。
凭君莫话封侯事,一将功成万骨枯。
梦里,小 F 成了一个给将军送密信的信使。
现在,有两封关乎国家生死的密信需要送到前线大将军帐下,路途凶险,时间紧迫。小
F 不因为自己的祸福而避趋之,勇敢地承担了这个任务。
不过,小 F 实在是太粗心了,他一不小心把两封密信中的一封给弄掉了。
小 F 偷偷打开了剩下的那封密信。 他发现一副十分详细的地图, 以及几句批文——原来
这是战场周围的情报地图。他仔细看后发现,在这张地图上标记了 ? 个从 1 到 ? 标号的
驿站,? − 1 条长度为 1 里的小道,每条小道双向连接两个不同的驿站,并且驿站之间可以
通过小道两两可达。
小 F 仔细辨认着上面的批注,突然明白了丢失的信的内容了。原来,每个驿站都可以驻
扎一个小队,每个小队可以控制距离不超过 ? 里的驿站。如果有驿站没被控制,就容易产
生危险——因此这种情况应该完全避免。 而那封丢失的密信里, 就装着朝廷数学重臣留下的
精妙的排布方案,也就是用了最少的小队来控制所有驿站。
小 F 知道,如果能计算出最优方案的话,也许他就能够将功赎过,免于死罪。他找到了
你,你能帮帮他吗?
当然, 小 F 在等待你的支援的过程中, 也许已经从图上观察出了一些可能会比较有用的
性质,他会通过一种特殊的方式告诉你。
【输入格式】
从文件 general.in 中读入数据。
输入第 1 行一个正整数 ?,?,?,代表驿站数,一支小队能够控制的最远距离,以及特
殊性质所代表的编号。关于特殊性质请参照数据范围。
输入第 2 行至第 ? 行,每行两个正整数 ? ? ,? ? ,表示在 ? ? 和 ? ? 间,有一条长度为
一里的小道。
【输出格式】
输出到文件 general.out 中。
输出一行,为最优方案下需要的小队数。
【样例 1 输入】
4 1 0
1 2
1 3
1 4
【样例 1 输出】
1
【样例 1 说明】
如图。由于一号节点到周围的点距离均是 1,因此可以控制所有驿站。
【样例 2 输入】
6 1 0
1 2
1 3
1 4
4 5
4 6
【样例 2 输出】
2
【样例 2 说明】
如图,和样例 1 类似。
【数据范围】
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解
决一部分测试数据。
关于 ? 的含义如下:
0:该测试点没有额外的特殊性质;
1最多 8 个点的所连接的小道超过 1 条;
? = 2:保证所有点到 1 号点的距离不超过 2。
每个测试点的数据规模及特点如下表:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define MAXN 100005
struct Edge{ int to,next; }e[MAXN << 1];
int head[MAXN],fa[MAXN],tot,que[MAXN],dis[MAXN];
bool exist[MAXN];
inline void read(int &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;
}
inline void Add_Edge(int u,int v){
e[++tot].to = v, e[tot].next = head[u], head[u] = tot;
e[++tot].to = u, e[tot].next = head[v], head[v] = tot;
}
void BFS(){
fa[1] = 1, que[1] = 1;
int top = 1,tail = 2;
while(top < tail){
int u = que[top++];
for(int i=head[u]; i; i=e[i].next){
int v = e[i].to;
if(v == fa[u]) continue;
fa[v] = u, que[tail++] = v;
}
}
}
void DFS(int u,int dep){
exist[u] = true, dis[u] = dep;
if(!dep) return;
for(int i=head[u]; i; i=e[i].next){
int v = e[i].to;
if(dis[v] < dis[u]-1 || !exist[v]) DFS(v,dep-1);
}
}
int main(int argc,char *argv[]){
freopen("general.in","r",stdin);
freopen("general.out","w",stdout);
int n,K,t;
read(n),read(K),read(t);
for(int u,v,i=1; i<=n-1; ++i){
read(u),read(v);
Add_Edge(u,v);
}
BFS();
memset(dis,0,sizeof dis );
int Ans = 0;
for(int i=n; i; --i){
int x = que[i];
if(!exist[x]){
++Ans;
for(int j=K; j; --j) x = fa[x];
dis[x] = K;
DFS(x,K);
}
}
printf("%d\n",Ans);
fclose(stdin); fclose(stdout);
return 0;
}
对于某个没有被控制起来的点,肯定是在他的k级祖先上设立小队更优—> 贪心(树形DP也可以吧)
只用exist数组是不行的,为什么要记录一个dis数组(记录里最近的小队的距离),如果不记录有可能会在他的k级祖先设立小队的时候会把他的兄弟的儿子堵死,导致另外多设一个小队。。。大概意会一下吧。。
T3
关于T3并不想说什么