Codeforces 758C:Unfair Poll(思维+模拟)
http://codeforces.com/problemset/problem/758/C
题意:教室里有n列m排,老师上课点名从第一列第一排开始往后点,直到点到第一列第m排,就从第二列第一排开始点,当点完第n列的名之后,接着点第n-1列的名。以此类推,就是从列上来看的话:1,2,3,4,……,n,n-1,n-2,……,1 ,2,……。这样的顺序点名。老师上课总共点k次名,问该课堂最多可以点同一个同学多少次,最少可以点同一个同学多少次,点了位置为(x,y)的同学多少次名。
思路:一遇到这种题目就有点血崩。确信以为是一条公式可以做出来的题目,然后写了一页草稿纸,连样例都过不了,不知所措。赛后想看看别人的公式,结果看过的都是通过计算+模拟的。既然用计算机了,在符合时间复杂度的情况下用模拟帮助计算,好像没什么问题啊。(为什么我就直接确信直接去推公式了,而且好像也不是第一次犯这种错误了啊)。
首先要注意点名的列顺序,好像挺多人都没看懂题目(就算我看懂了然并卵),假设n是4,那么顺序是1,2,3,4,3,2,1,2,3,……。这样的,所以从列来看一个周期是(n+n-2),因为当n = 1的时候这条式子等于0,所以要特判n = 1的情况。如果n = 1的话,那么每次都是点这一排了,最多的人是算了k / m次,剩下的用k % m从第一排去模拟加上,直到那个余数变成0为止。如果n > 1的话,那么每个周期点第1列和第n列的次数是只有一次,而点2~n-1列的次数是有两次,那么可以点的周期数 = k / m / (n+n-2),然后剩下的用k % (m * (n+n-2))去模拟。因为初始是从第一列开始的,所以从第一列开始扫起。
1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 #include <string> 6 #include <cmath> 7 #include <queue> 8 #include <vector> 9 #include <map> 10 #include <set> 11 #include <stack> 12 using namespace std; 13 #define INF 0x3f3f3f3f 14 #define N 100010 15 typedef long long LL; 16 LL mp[105][105]; 17 18 int main() { 19 int x, y, n, m; 20 LL k; 21 cin >> n >> m >> k >> x >> y; 22 LL mi = -1, ma = -1; 23 if(n == 1) { 24 LL yu = k % m; 25 LL cnt = k / m; 26 for(int i = 1; i <= m; i++) mp[1][i] += cnt; 27 for(int i = 1; i <= m; i++) 28 if(yu) mp[1][i]++, yu--; 29 } else { 30 LL num = (n + n - 2) * m; 31 LL cnt = k / num; // 周期数 32 LL yu = k % num; 33 for(int i = 1; i <= n; i++) 34 for(int j = 1; j <= m; j++) 35 if(i == 1 || i == n) mp[i][j] += cnt; 36 else mp[i][j] += cnt * 2; 37 for(int i = 1; i <= n; i++) 38 for(int j = 1; j <= m; j++) 39 if(yu) mp[i][j]++, yu--; 40 for(int i = n - 1; i >= 1; i--) 41 for(int j = 1; j <= m; j++) 42 if(yu) mp[i][j]++, yu--; 43 } 44 for(int i = 1; i <= n; i++) { 45 for(int j = 1; j <= m; j++) { 46 if(ma < mp[i][j]) ma = mp[i][j]; 47 if(mi == -1 || mi > mp[i][j]) mi = mp[i][j]; 48 } 49 } 50 printf("%I64d %I64d %I64d\n", ma, mi, mp[x][y]); 51 return 0; 52 }