11.3 Luogu mNOIP 模拟赛 Day 2

//本文中所有题目由fstqwq大佬提供

入阵曲(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 。


【数据范围与约定】

  子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解 决一部分测试数据。 每个测试点的数据规模及特点如下表:

  

  特殊性质:保证所有 𝑎𝑖,𝑗 均相同。

Solution:

  60分做法:   n <= 80。时间复杂度O(n^4)

        我们发现最后的两重循环一直在计算重复的内容,很容易想到记忆化来达到优化的目的。

        我们可以维护一个前缀和,用二位前缀和计算(i,j,a,b): 

        cnt[a][b]-cnt[i-1][b]-cnt[a][j-1]+cnt[i-1][j-1]

 

 1 #include<cstdio>
 2 #define MAXN 405
 3 #define ll long long
 4 int n,m,k,ans=0;
 5 ll cnt[MAXN][MAXN];
 6 int Map[MAXN][MAXN];
 7 int main(){
 8     int n,m,k;
 9     scanf("%d%d%d",&n,&m,&k);
10     for(int i=1;i<=n;i++)
11         for(int j=1;j<=m;j++){
12             scanf("%d",&Map[i][j]);
13             cnt[i][j]=Map[i][j]+cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1];
14         }
15     for(int i=1;i<=n;i++)
16         for(int j=1;j<=m;j++)
17             for(int a=i;a<=n;a++)
18                 for(int b=j;b<=m;b++)
19                     if((cnt[a][b]-cnt[i-1][b]-cnt[a][j-1]+cnt[i-1][j-1])%k==0) ans++;
20     printf("%d",ans);
21 }
60分代码

 100分做法:n <= 400。时间复杂度O(n^3)。

       对于一维的情况,我们可以参考这个blog(http://blog.csdn.net/qq_35776409/article/details/78226120)。

       可以知道 ,在一维上,枚举后,暴力O(n^3),前缀和维护(n^2);

       能不能不枚举呢?
       观察到,在模 𝑘 意义下相同的前缀和,任取两个相减必定是 𝑘 的倍数。
       把零作为其中一个元素,问题等价于统计前缀和中模 𝑘 相同的数的对数;
       直接对于每个余数统计个数就好了,在 𝑘 个元素存的下的情况下为 𝑂(𝑛)

      所以:

      100 分做法:
       拓展到二维:压行。
       枚举所有连续的列,压成一行处理。
       答案需要 long long。

将军令(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。

  每个测试点的数据规模及特点如下表:

 

Solution:

 

Code:       

 1 #include<cstdio>
 2 #include<cstring> 
 3 #define MAXN 100005
 4 #define MAXE 200005
 5 #define debug 0
 6 using namespace std;
 7 int n,k,t,ans=0;
 8 struct egde{
 9     int to,next;
10 }e[MAXE];
11 int h[MAXN],cnt=0;
12 #if debug
13     int dfs_cnt=0,bfs_cnt=0;
14 #endif
15 inline void init(int u,int v){
16     cnt++,e[cnt].to=v,e[cnt].next=h[u],h[u]=cnt;
17     cnt++,e[cnt].to=u,e[cnt].next=h[v],h[v]=cnt;
18     #if debug
19         //printf("u=%d v=%d\n",h[u],h[v]);
20     #endif
21 }
22 int q[MAXN],d[MAXN],head,tail;
23 inline void bfs(){
24     #if debug
25         bfs_cnt++;
26         printf("bfs=%d\n",bfs_cnt);
27     #endif
28     memset(d,0,sizeof(d));
29     head=1;tail=0;
30     q[++tail]=1,d[1]=1;tail++;
31     while(head<tail){
32         int u=q[head++];
33         #if debug
34             printf("u=%d\n",u);
35         #endif
36         for(int i=h[u];i;i=e[i].next){
37             int v=e[i].to;
38             if(d[u]!=v) d[v]=u,q[tail++]=v;
39         }
40     }
41 }
42 int vis[MAXN],len,det[MAXN];
43 inline void dfs(int u,int x){
44     #if debug
45         dfs_cnt++;
46         printf("dfs=%d\n",dfs_cnt);
47     #endif
48     vis[u]=1;det[u]=x;len++;
49     if(x==0) return ;
50     for(int i=h[u];i;i=e[i].next){
51         int v=e[i].to;
52         if(!vis[v]||det[v]<det[u]-1) dfs(v,x-1);
53     }
54 }
55 int main(){
56     //freopen("233.out","w",stdout);
57     memset(det,0,sizeof(det));memset(h,0,sizeof(h));memset(vis,0,sizeof(vis));
58     scanf("%d%d%d",&n,&k,&t);
59     int time=n-1;
60     while(time--){
61         int x,y;scanf("%d%d",&x,&y);
62         init(x,y);
63     }
64     bfs();
65     memset(det,0,sizeof(det));
66     #if debug
67         //for(int i=1;i<=n;i++) printf("q[%d]=%d",i,q[i]);
68     #endif
69     for(int i=n;i;i--){
70         int top=q[i];
71         #if debug
72             printf("top=%d\n",top);
73         #endif
74         if(!vis[top]) {
75             ans++;
76             for(int j=k;j;j--) top=d[top];
77             det[top]=k;dfs(top,k);
78         }
79     }
80     printf("%d",ans);
81     return 0;
82 } 

Other:

  看了假题。

  慎用while。特别是当你写完了while(n--)之后又for(int i=1;i<=n;i++)。

  就等于写了假题了 ...T T

星空(starlit)

【题目描述】

  命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷。 你来过,然后你走后,只留下星空。

  逃不掉的那一天还是来了,小 F 看着夜空发呆。

  天上空荡荡的,没有一颗星星——大概是因为天上吹不散的乌云吧。

  心里吹不散的乌云,就让它在那里吧,反正也没有机会去改变什么了。

  小 C 拿来了一长串星型小灯泡,假装是星星,递给小 F,想让小 F 开心一点。不过,有 着强迫症的小 F 发现,这串一共 n 个灯泡的灯泡串上有 k 个灯泡没有被点亮。小 F 决定 和小 C 一起把这个灯泡串全部点亮。

不过,也许是因为过于笨拙,小 F 只能将其中连续一段的灯泡状态给翻转——点亮暗灯 泡,熄灭亮灯泡。经过摸索,小 F 发现他一共能够翻转 m 种长度的灯泡段中灯泡的状态。

  小 C 和小 F 最终花了很长很长很长很长很长很长的时间把所有灯泡给全部点亮了。他 们想知道他们是不是蠢了,因此他们找到了你,让你帮忙算算:在最优的情况下,至少需要 几次操作才能把整个灯泡串给点亮?

【输入格式】

  从文件 starlit.in 中读入数据。

  输入第 1 行三个正整数 n,k,m

  输入第 2 行 k 个正整数,第 i 个数表示第 i 个被没点亮的灯泡的位置 ai

  输入第 3 行 m 个正整数,第 i 个数表示第 i 种操作的长度 bi

  保证所有 bi 互不相同;保证对于 1 <= i < k,有 ai< ai+1;保证输入数据有解。

【输出格式】

  输出到文件 starlit.out 中。

  输出一行一个非负整数,表示最少操作次数。

【样例 1 输入】
  5 2 2
  1 5
  3 4
【样例 1 输出】
  2

【样例 1 解释】

  

【样例 2
  见选手目录下的 starlit/starlit2.in starlit/starlit2.ans

【数据范围】  

  子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解 决一部分测试数据。

  每个测试点的数据规模及特点如下表:

特殊性质: 保证答案小于 4。 

Solution:

  8分做法:

1 #include<cstdio>
2 using namespace std;
3 int main(){
4     puts("3");
5     return 0;
6 }
8分

  12分做法:

1 #include<cstdio>
2 using namespace std;
3 int main(){
4     puts("2");
5     return 0;
6 }
12分

其余还不肥...

posted @ 2017-11-03 07:55  drizzly  阅读(445)  评论(0编辑  收藏  举报