T1 荷花池塘 (pool)
题目背景
于大夫建造了一个美丽的池塘,用来让自己愉快的玩耍。这个长方形的池子被分割成了M 行和N 列的正方形格子。池塘中有些地方是可以跳上的荷叶,有些地方是不能放置荷叶也不能跳上的岩石,其他地方是池水(当然于大夫也是不能游泳的)。
题目描述
于大夫十分有趣,他在池塘跳跃的方式和象棋中的马一样可以向八个方向走日字形,而且于大夫只能跳上荷叶。 于大夫每天从一个给定的有荷叶的地方出发,试图到达另一个给定的有荷叶的地方。但有一天他发现自己无论如何也不能到达目的地了,除非再在水中放置几个荷叶。于大夫想让你告诉他,最少还需放置几片荷叶?在放置荷叶最少的前提下,最少需要几步能到达目的地?
输入输出格式
输入格式:
第1 行: 两个整数M , N 第2…M+1 行:第i + 1 行有N 个整数,表示该位置的状态:
0 为水; 1 为荷叶; 2 为岩石; 3 为于大夫开始的位置; 4 为于大夫要去的目标位置.
输出格式:
一行两个整数空格隔开:分别为最少放置荷叶数和最小放置为前提下最少步数。(,如果无 论如何也不能到达请输出-1 -1)。
输入输出样例
输入样例#1: 复制
input1:
4 8
0 0 0 1 0 0 0 0
0 0 0 0 0 2 0 1
0 0 0 0 0 4 0 0
3 0 0 0 0 0 1 0
输出样例#1: 复制
2 6
说明
10%的数据n,m<=4
30%的数据n,m<=10
50%的数据n,m<=30
70%的数据n,m<=50
100%的数据n,m<=100
所以这是道爆搜题…
可算是双关键字爆搜吧,我们可以用priority_queue来维护我们的BFS队列,定义所需荷叶数的优先级大于路径长度,因此我们第一次搜到终点,此时所需荷叶数最少,路径长度最短,一定是最优解
AC Code:
#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1 ; rg char ch = getchar() ; while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0' ; ch = getchar();}return x * w;}
char ss[1000];
int s1 , s2 , t1 , t2 , n , m;
bool vis[102][102];
int f[102][102];
int fx[8] = {2 , 2 , 1 , 1 , -1 , -1 , -2 , -2} , fy[8] = {1 , -1 , 2 , -2 , 2 , -2 , 1 , -1};
struct node{
int x , y , len , num;
friend bool operator<(const node a , const node b){
return (a.num > b.num) || (a.num == b.num && a.len > b.len);
}
};
node bfs(int x , int y){
priority_queue<node> q;
q.push((node){x , y , 0 , 0});
while (!q.empty()){
node now = q.top();
q.pop();
rg int x = now.x , y = now.y , len = now.len , num = now.num;
if (vis[x][y]) continue;
vis[x][y] = 1;
if (x == t1 && y == t2) return now;
for (rg int i = 0 ; i < 8 ; ++i){
rg int xx = x + fx[i] , yy = y + fy[i];
if (xx < 0 || xx > n || yy < 0 || yy > m) continue;
if (f[xx][yy] == 2) continue;
else if (f[xx][yy] == 1 || f[xx][yy] == 4)
q.push((node){xx , yy , len + 1 , num});
else if (!f[xx][yy])
q.push((node){xx , yy , len + 1 , num + 1});
}
}
return (node){0 , 0 , -1 , -1};
}
int main(){
n = read() , m = read();
for (rg int i = 1 ; i <= n ; ++i){
for (rg int j = 1 ; j <= m ; ++j){
f[i][j] = read();
if (f[i][j] == 3) s1 = i , s2 = j;
if (f[i][j] == 4) t1 = i , t2 = j;
}
}
node ans = bfs(s1 , s2);
cout << ans.num << ' ' << ans.len;
return 0;
}
T2 友好数对 (num)
题目描述
如果一个数a,能由一个数b 旋转得到,那么我们称<a,b>为友好数对,如12345 和45123为友好数对,12345 和54321 不为友好数对。
给出两个正整数L,R,求有多少友好数对<a,b>,满足L<=a<b<=R。
输入输出格式
输入格式:
第一行一个整数T,表示数据组数,每组数据两个正整数L,R。
输出格式:
对于每组数据,输出一个整数表示答案。
输入输出样例
输入样例#1: 复制
4
1 9
10 40
100 500
1111 2222
输出样例#1: 复制
0
3
156
287
说明
对于30%的数据满足L,R<=1000
对于100%的数据满足L,R<=2000000,T<=30
这道题的旋转定义很迷啊…
由题意得,如果A和B互为友好数对,那么A和B的友好数对也为友好数对,并且这些互为友好数对所组成的集合之间的交集为空,也就是说事实上每个点只会扫描一次,因此可以暴力O(n)(好吧,其实还要优化一下,我们预处理10的幂来加快计算速度)
AC Code:
#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 5000005
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1 ; rg char ch = getchar() ; while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0' ; ch = getchar();}return x * w;}
bool vis[maxn];
int p[20];
int main(){
rg int t = read();
p[0] = 1;
for (rg int i = 1 ; i <= 10 ; ++i)
p[i] = p[i - 1] * 10;
ll ans = 0;
while (t--){
ans = 0;
int l = read() , r = read();
for (rg int i = l ; i <= r ; ++i) vis[i] = 0;
for (int i = l ; i <= r ; ++i){
if (vis[i]) continue;
vis[i] = 1;
int x = i , tmp = 0 , len = 0 , tot = 0 , res = 0;
while (x){
x /= 10;
++len;
}
for (rg int j = 1 ; j < len ; ++j){
rg int tmp = i / p[j] + i % p[j] * p[len - j];
if (tmp >= l && tmp <= r && !vis[tmp]){
vis[tmp] = 1 , ans += ++res;
}
}
/*while (x >= 10){
int y = x % 10;
x /= 10;
tmp = tmp * 10 + y;
++tot;
int tmp1 = tmp;
for (rg int j = 1 ; j <= len - tot ; ++j)
tmp1 *= 10;
if (tmp1 + x >= l && tmp1 + x <= r && !vis[tmp1 + x]){
vis[tmp1 + x] = 1;
ans += ++res;
}
}*/
}
cout << ans << endl;
}
}
T3 排列 (pai)(好题!)
题目描述
对于一个1->n的排列 ,定义A中的一个位置i是好的,当且仅当Ai-1>Ai 或者Ai+1>Ai。对于一个排列A,假如有不少于k个位置是好的,那么称A是一个好的排列。
现在有q个询问,每个询问给定n,k,问有多少排列是好的。答案对10^9+7取模。
输入输出格式
输入格式:
首先输入q。
接下来输入q个询问n,k
输出格式:
输出q行,每行一个整数代表答案。
输入输出样例
输入样例#1: 复制
8
4 3
6 4
10 7
20 14
50 40
100 72
1000 900
3000 2000
输出样例#1: 复制
8
448
1433856
868137807
908422882
609421284
150877522
216180189
说明
对于20%的数据,n<=10,q=1
对于40%的数据,n<=20,q=1
对于60%的数据,n<=100
对于100%的数据,n,k<=3000,q<=10000
由于这道题的数据范围,n、k满足n ^ 2,而q又比较大,我们比较容易想到递推预处理来o1查询,但是式子该怎么推呢?
对于数n和1 ~ n - 1的序列来说,n肯定是比1 ~ n - 1中的any数都要大的,但是不一定它会对答案造成贡献,我们反向思考一下,因为题目的定义是
对于一个1->n的排列 ,定义A中的一个位置i是好的,当且仅当Ai-1>Ai 或者Ai+1>Ai
好处我们反而不怎么好定义,但是坏处就简单多了,也就是一个峰大于左右两边的数(易证峰不是连续的),现在我们要将n加入到1 ~ n - 1中,试想,如果它加到一个峰的旁边,它自己必然是一个峰,但是原来的峰也必然会消失,导致答案是不变的,如果它不加到峰的旁边,那么就多了一个峰。
推一下式子,我们设f[i][j]为i个数峰为j个的排列数,那么我们考虑上文的两种情况:
- 加到峰的旁边,不增加峰
f[i][j] += f[i - 1][j](没有增加峰)* (i - (i - j) << 1 + 2)(若在不是峰的旁边肯定两边都得不是峰) - 加到谷的旁边
f[i][j] += f[i - 1][j - 1] * (i - j) << 1)
AC Code:
#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
#define mod 1000000007
using namespace std;
il int read(){rg int x = 0 , w = 1 ; rg char ch = getchar() ; while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0' ; ch = getchar();}return x * w;}
ll f[3002][3002];
int main(){
rg int q = read();
f[2][1] = 2;
for (int i = 3 ; i <= 3000 ; ++i)
for (int j = 1 ; j <= i ; ++j){
f[i][j] = (f[i - 1][j - 1] * (i - j) % mod * 2 % mod + f[i - 1][j] * (i - ((i - j) << 1) + 2) % mod) % mod;
}
rg int n , k;
for (rg int i = 1 ; i <= q ; ++i){
n = read() , k = read();
if (n == 1){
printf("%lld\n" , 1);
continue;
}
ll ans = 0;
for (rg int i = k ; i <= n; ++i)
ans = (ans + f[n][i] % mod) % mod;
printf("%lld\n" , ans);
}
return 0;
}