T1 problem 100
从n个位置里面先选择m个固定下来,剩下的n-m个就相当于错排问题了。从n个位置选出m个就是C(n, m) 。所以预处理阶乘及其逆元、错排公式,然后对于每个询问O(1)回答即可。注意0的情况。
错排:d[n] = (n-1)*[d(n-1)+d(n-2)]
最开始的程序很不成熟,没有预处理什么阶乘、错排,而且对于逆元也不熟。后来为了在时间上优化,想了很多办法,利用上这些,终于完成了。
#include<bits/stdc++.h>
using namespace std;
const int p = 1e9+7;
#define ll long long
#define maxn (1000000+5)
int t, n, m;
ll fact[maxn], d[maxn], k[maxn];
template <typename T> T read(){
T nx(0), fx(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fx = -1;
for(; isdigit(ch); ch = getchar()) nx = nx*10 + ch-48;
return nx*fx;
}
void fac(){
fact[1] = 1LL;
for(int i = 2; i < maxn; i++)
fact[i] = fact[i-1]%p*i%p;
}
void cp(){
d[0] = 1, d[1] = 0, d[2] = 1;
for(int i = 3; i < maxn; i++){
d[i] = (ll)(i-1)%p * (d[i-1]+d[i-2])%p;
}
}
ll ksm(ll x){
ll ret = 1LL;
ll b = (ll)(p-2);
while(b){
if(b & 1LL) ret = (ret * x) % (ll)p;
x = (x * x) % (ll)p;
b >>= 1;
}
return ret%(ll)p;
}
void ny(){
k[0] = 1;
for(int i = 1; i < maxn; i++) k[i] = ksm(fact[i]) %(ll)p;
}
ll C(int a, int b){
return fact[a] % (ll)p * k[b] %(ll)p * k[a-b]%(ll)p;
}
int main(){
freopen("problem.in", "r", stdin);
freopen("problem.out","w",stdout);
fac(); cp(); ny();
t = read<int>();
while(t--){
/* for(int i = 1; i <= 1000000; i++){
printf("%lld %lld %lld\n", fact[i], d[i], k[i]);
}
*/
n = read<int>();
m = read<int>();
// printf("%lld\n", C(1, 1));
printf("%lld\n", C(n, m)%(ll)p*d[n-m]%(ll)p);
}
return 0;
}
/*
1
10000 5000
*/
T2 tracks 15
想了好久,最后还是只判断了1、2、3的情况,一直在往联通块上想,不过最后还是没发现那个巧妙的事实,不过这样一来也让我注意到有时候想不出思路可以考虑从答案状态反过来思考,说不定就有思路或者正解了。
solution:
贪心.考虑观察题目性质:
1.最后一只经过草地的动物可以直接从输入中得出,那就是左上角和右下
角的动物.
2.在一种可能的最小解中,一定不会存在两只同种的动物依次穿过草地.所
以对于同种动物我们会尽量扩展它所在的联通块.
3.于是我们考虑每一次从左上角尽量多的扩展同种动物的联通块,直到不能扩展.对于已经扩展的小格,我们可以用’* ’表示,意味着它可以在今后的任何一次扩展中到达.于是我们对于一张n*m的图一次扩展是O(n* m)的,最多会有n*m次扩展.
如何线性求解呢?
我们可以一次将图中所有的联通块求出来,把所有相邻的同种脚印的格子缩成一个点.那么答案就是离左上角所在联通块距离最远的联通块的距离+1.
所以也不是那么难啊,以后还要多细想、深度思考,模拟考试的时候还是多注重思考吧。
终于写完了,复习了bfs找最长路,还有中间的思考过程都是很有收获的
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define LL long long
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define Forr(i, a, b) for(int i = (a); i >= (int)(b); --i)
using namespace std;
#define N (4000+5)
struct node{
int x, y;
};
int n, m, fx[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
char mp[N][N];
queue<node> q[2];
int dis[N][N], ans;
void BFS(){
dis[1][1] = 1;
q[0].push((node){1, 1});
node o, u;
while(!q[0].empty()){
For(i, 0, 1) while(!q[i].empty()){
o = q[i].front(); q[i].pop();
ans = max(ans, dis[o.x][o.y]);
For(j, 0, 3){
u = (node){o.x + fx[j][0], o.y + fx[j][1]};
if(dis[u.x][u.y] || mp[u.x][u.y]=='.') continue;
if(mp[u.x][u.y] == mp[o.x][o.y]){
dis[u.x][u.y] = dis[o.x][o.y];
q[i].push(u);
}else{
dis[u.x][u.y] = dis[o.x][o.y] + 1;
q[i^1].push(u);
}
}
}
}
}
int main(){
freopen("tracks.in", "r", stdin);
freopen("tracks.out", "w", stdout);
scanf("%d%d", &n, &m);
For(i, 1, n) scanf("%s", mp[i] + 1);
For(i, 0, n+1) mp[i][0] = mp[i][m+1] = '.';
For(i, 0, m+1) mp[0][i] = mp[n+1][i] = '.';
BFS();
printf("%d\n", ans);
return 0;
}
T3 ballmachine 0(其实是20分)
对于往哪一棵子树走要排序找出那个有最小节点的子树!要排序!要排序!
我没排序不然还可以得分。。思路方面也没问题,倍增.
考虑如何快速加入:在对每个点的所有孩子节点排完序后,再做一次DFS确定在整棵树为空时球的掉落顺序.这个顺序是节点的优先级顺序,所以我们只需要在O(log(N))的时间内找到第一个没有球的节点就是答案了.优先队列、set、线段树都可以.
考虑如何快速移除:预处理倍增数组,因为如果一个节点的某一个祖先节点是有球的,那么从该节点到那个祖先节点之间所有的点都是有球的,用倍增可以O(log(N))求了.
暂时未写正确的程序,等下看正解再修改或重写。
#include<bits/stdc++.h>
using namespace std;
#define maxn (100000+5)
vector<int> son[maxn];
int n, q, rt, cnt, ans, fl;
int head[maxn], p[maxn][20], vis[maxn], ball[maxn], deep[maxn];
struct node{
int to, next;
}e[maxn];
template <typename T> T read(){
T nx(0), fx(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fx = -1;
for(; isdigit(ch); ch = getchar()) nx = nx*10 + ch-48;
return nx * fx;
}
void add(int x,int y){
e[++cnt] = (node){y,head[x]};
head[x] = cnt;
}
void dfs(int u){
for(int i = head[u]; i; i = e[i].next){
int v = e[i].to;
if(!deep[v]){
deep[v] = deep[u] + 1;
p[v][0] = u;
dfs(v);
}
}
}
void pre(){
for(int j = 1; j <= 19; j++)
for(int i = 1; i <= n; i++)
if(p[i][j-1]) p[i][j] = p[p[i][j-1]][j-1];
}
void inc(int now){
// printf("%d %d\n", now, ball[now]);
if(ball[now] || fl) return;
if(son[now].size() == 1){
ball[now] = 1;
fl = 1;
ans = now;
return;
}
int f = 0;
sort(son[now].begin(), son[now].end());
for(int i = 0; i < son[now].size(); i++) printf("%d ", son[now][i]);
printf("\n");
for(int i = 1; i < son[now].size(); i++){
int vv = son[now][i];
if(!ball[vv]){
f = 1;
inc(vv);
}
}
if(!f){
ball[now] = 1;
ans = now;
fl = 1;
return;
}
}
void dec(int now){
int k, k2;
for(int j = 19; j >= 0; j--){
if(p[now][j] && ball[p[now][j]]){
k = p[now][j];
while(p[k][0] && ball[p[k][0]]){
k = p[k][0];
}
ball[k] = 0;
break;
}
}
printf("%d\n", deep[now] - deep[k]);
}
int main(){
freopen("ballmachine.in", "r", stdin);
freopen("ballmachine.out","w",stdout);
n = read<int>(), q = read<int>();
for(int i = 1; i <= n; i++){
int pr;
pr = read<int>();
if(!pr){
rt = i;
add(i, i);
}
else add(pr, i);
}
deep[rt] = 1;
dfs(rt);
pre();
// for(int i = 1; i <= n; i++) printf("%d ", deep[i]);
for(int i = 1; i <= n; i++) son[i].push_back(i);
for(int i = 1; i <= n; i++){
for(int j = 0; j <= 19; j++){
if(!p[i][j]) break;
int fa = p[i][j];
if(deep[i] == deep[fa]+1)son[fa].push_back(i);
}
}
for(int i = 1; i <= q; i++){
int x, y;
x = read<int>(), y = read<int>();
if(x == 1){
while(y--){
fl = ans = 0;
inc(1);
if(y == 0) printf("%d\n", ans);
}
}
else if(x == 2){
if(!ball[p[y][0]]){
ball[y] = 0;
printf("0\n");
}
else{
dec(y);
}
}
}
return 0;
}