记录一下
A
题意:给定三维空间里一个立方体,让你在立方体表面或者内部找到一个点
T
使得与点
思路:一个开头向上的二次函数,肯定有最小值。
E
题意:面值为
1、2、3
的硬币分别有
a1、a2、a3
个,问你可以得到多少个不同价值。
思路:分类讨论,注意
a1==0
的情况。
F
题意:给定一个无向连通图,
Q
次查询,每次查询问你
思路:因为边数 — 点数
<=200
,这样的话我们先用并查集找到一棵生成树,这样只剩下
<=200
非树边。
u−>v
的最优解要么全在树上,要么借助非树边。
对于非树边每一个节点,跑一发
BFS
来预处理最短路,最多跑
400
次。
在树边的情况,就是
LCA
的经典问题了,反之枚举中间点即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 200;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {
x += y;
if(x >= MOD) x -= MOD;
}
int father[MAXN];
int Find(int p) {
int t, son = p;
while(p != father[p]) { p = father[p]; }
while(son != p) { t = father[son]; father[son] = p; son = t; }
return p;
}
struct Edge {
int from, to, next;
};
Edge edge[MAXN * 2];
int head[MAXN], edgenum;
void init() { memset(head, -1, sizeof(head)); edgenum = 0; }
void addEdge(int u, int v) {
Edge E = {u, v, head[u]};
edge[edgenum] = E;
head[u] = edgenum++;
}
bool vis[MAXN];
int node[MAXN], top;
int dist[300][MAXN];
int n, m, q;
void BFS(int s) {
for(int i = 1; i <= n; i++) {
vis[i] = false;
}
queue<int> Q;
vis[node[s]] = true; dist[s][node[s]] = 0; Q.push(node[s]);
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(!vis[v]) {
dist[s][v] = dist[s][u] + 1;
vis[v] = true;
Q.push(v);
}
}
}
}
vector<int> G[MAXN];
int vs[MAXN << 1], depth[MAXN << 1], id[MAXN];
int dfs_clock;
int dp[MAXN << 1][30];
int D[MAXN];
void DFS(int u, int fa, int d) {
id[u] = dfs_clock; vs[dfs_clock] = u;
depth[dfs_clock++] = d;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(v == fa) continue;
D[v] = D[u] + 1;
DFS(v, u, d + 1);
vs[dfs_clock] = u;
depth[dfs_clock++] = d;
}
}
void find_depth() {
dfs_clock = 1;
memset(vs, 0, sizeof(vs));
memset(id, 0, sizeof(id));
memset(depth, 0, sizeof(depth));
memset(D, 0, sizeof(D));
DFS(1, -1, 0);
}
void RMQ_init(int N) {
for(int i = 1; i <= N; i++) {
dp[i][0] = i;
}
for(int j = 1; (1 << j) <= N; j++) {
for(int i = 1; i + (1 << j) - 1 <= N; i++) {
int a = dp[i][j - 1];
int b = dp[i + (1 << (j - 1))][j - 1];
dp[i][j] = depth[a] < depth[b] ? a : b;
}
}
}
int Query(int L, int R) {
int k = 0;
while((1 << (k + 1)) <= R - L + 1) k++;
int a = dp[L][k];
int b = dp[R - (1 << k) + 1][k];
return depth[a] < depth[b] ? a : b;
}
int LCA(int u, int v) {
int x = id[u];
int y = id[v];
return x < y ? vs[Query(x, y)] : vs[Query(y, x)];
}
int main()
{
while(scanf("%d%d%d", &n, &m, &q) != EOF) {
init(); top = 0;
for(int i = 1; i <= n; i++) {
father[i] = i;
G[i].clear();
}
for(int i = 1; i <= m; i++) {
int u, v; scanf("%d%d", &u, &v);
addEdge(u, v); addEdge(v, u);
int fu = Find(u);
int fv = Find(v);
if(fu != fv) {
father[fu] = fv;
G[u].push_back(v);
G[v].push_back(u);
}
else {
node[top++] = u;
node[top++] = v;
}
}
find_depth(); RMQ_init(dfs_clock - 1);
sort(node, node + top);
top = unique(node, node + top) - node;
for(int i = 0; i < top; i++) {
BFS(i);
}
while(q--) {
int u, v; scanf("%d%d", &u, &v);
int ans = D[u] + D[v] - 2 * D[LCA(u, v)];
for(int i = 0; i < top; i++) {
ans = min(ans, dist[i][u] + dist[i][v]);
}
printf("%d\n", ans);
}
}
return 0;
}
H
题意:给定一个无向连通图,每两个节点之间都有
2∗c[i]
条不同的边相连。问你从节点
1
出发经过所有边一次(且只有一次)再回到节点
思路:
dp[i]
表示从节点
i
出发的方案数。
一、首先考虑
假设有
N
个孩子,第
则共有回路
num[j]=son[j]2
,组成这些回路的边都不相同的。
这样方案数是
(∑Nj=1num[j])!∏Nj=1(num[j]!)∗∏Nj=1(son[j]!)
二、考虑第
j
个孩子如何走完它的子树,可能没有走完
第
j
个孩子与父亲有
方案数
Cnum[j]−1cnt[j]+num[j]−1
dp[i]=(∑Nj=1num[j])!∏Nj=1(num[j]!)∗∏Nj=1(son[j]!)∗∏Nj=1Cnum[j]−1cnt[j]+num[j]−1
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {
x += y;
if(x >= MOD) x -= MOD;
}
struct Edge {
int from, to, val, next;
};
Edge edge[MAXN * 2];
int head[MAXN], edgenum;
void init() {memset(head, -1, sizeof(head)); edgenum = 0;}
void addEdge(int u, int v, int w) {
Edge E = {u, v, w, head[u]};
edge[edgenum] = E;
head[u] = edgenum++;
}
LL pow_mod(LL a, int n) {
LL ans = 1;
while(n) {
if(n & 1) {
ans = ans * a % MOD;
}
a = a * a % MOD;
n >>= 1;
}
return ans;
}
LL dp[MAXN];
LL fac[2000000 + 10];
int sum[MAXN];
LL C(int n, int m) {
return fac[n] * pow_mod(fac[n - m], MOD - 2) % MOD * pow_mod(fac[m], MOD - 2) % MOD;
}
void DFS(int u, int fa) {
dp[u] = 1; sum[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(v == fa) continue;
DFS(v, u);
sum[u] += edge[i].val;
dp[u] = dp[u] * dp[v] % MOD;
dp[u] = dp[u] * C(sum[v] + edge[i].val - 1, sum[v]) % MOD;
dp[u] = dp[u] * fac[edge[i].val * 2] % MOD;
dp[u] = dp[u] * pow_mod(fac[edge[i].val], MOD - 2) % MOD;
}
dp[u] = dp[u] * fac[sum[u]] % MOD;
}
int main()
{
fac[0] = 1;
for(int i = 1; i <= 2000000; i++) {
fac[i] = fac[i - 1] * i % MOD;
}
int n;
while(scanf("%d", &n) != EOF) {
init();
for(int i = 1; i <= n - 1; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addEdge(u, v, w);
addEdge(v, u, w);
}
DFS(1, -1);
printf("%lld\n", dp[1]);
}
return 0;
}
I
题意:有
N
个数,第
思路:
N
很小,我们可以先确定
然后我们可以得到一个名次数组
r[c[i]]=i,这样
第
c[i]
小的元素是第
r[c[i]]
个数。
对于确定的全排列,按照名次数组
r[]
的顺序
(i<j)
,我们再加一个位置上的限制
即:若
r[i]>r[j]
,那么
xi>=xj
,反之
xi<xj
。
设置
dp[i][j]
为第
i
小的数取
r[i−1]>r[i]
,
dp[i][j]=∑jk=1dp[i−1][k]
r[i−1]<r[i]
,
dp[i][j]=∑j−1k=1dp[i−1][k]
暴力统计肯定会
T
的,只需在
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 200;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {
x += y;
if(x >= MOD) x -= MOD;
}
LL ans[6];
LL dp[6][1000 + 10];
LL sum[6][1000 + 10];
int a[6], b[6];
int n;
int c[6], L[6], r[6];
void Work() {
int len = 0;
for(int i = 1; i <= n; i++) {
L[i] = 1;
for(int j = 1; j < i; j++) {
if(c[j] < c[i]) {
L[i] = max(L[i], L[j] + 1);
}
}
len = max(len, L[i]);
for(int j = 0; j <= 1000; j++) {
dp[i][j] = 0;
}
}
for(int i = 1; i <= n; i++) {
r[c[i]] = i;
}
for(int i = a[r[1]]; i <= b[r[1]]; i++) {
dp[1][i] = 1;
}
sum[1][0] = 0;
for(int i = 1; i <= 1000; i++) {
sum[1][i] = sum[1][i - 1] + dp[1][i];
}
for(int i = 2; i <= n; i++) {
for(int j = a[r[i]]; j <= b[r[i]]; j++) {
if(r[i - 1] > r[i]) {
dp[i][j] = sum[i - 1][j];
}
else {
dp[i][j] = sum[i - 1][j - 1];
}
}
sum[i][0] = 0;
for(int j = 1; j <= 1000; j++) {
sum[i][j] = sum[i][j - 1] + dp[i][j];
}
}
ans[len] += sum[n][b[r[n]]];
//printf("%d %d\n", len, sum[n][b[r[n]]]);
}
int main()
{
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= n; i++) {
scanf("%d%d", &a[i], &b[i]);
ans[i] = 0; c[i] = i;
}
do {
Work();
}while(next_permutation(c + 1, c + n + 1));
for(int i = 1; i <= n; i++) {
if(i > 1) printf(" ");
printf("%lld", ans[i]);
}
printf("\n");
}
return 0;
}
J
题意:
n∗n
矩阵,两种操作
1、
lrd
, 位置
(x,y)
的元素被位置
(x,(y+d)%n)
的元素替换,
l<=x<=r,0<=y<n
。
2、
lrd
, 位置
(x,y)
的元素被位置
((x+d)%n,y)
的元素替换,
0<=x<n,l<=y<=r
。
用十字链表记录,发现每次对于
l
行(列)和
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {
x += y;
if(x >= MOD) x -= MOD;
}
struct Node {
int l, r, u, d, v;
};
Node a[201 * 201];
int id[201][201];
int n, q, root;
void Init() {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
if(i == 0) {
a[id[i][j]].u = -1;
}
else {
a[id[i][j]].u = id[i - 1][j];
}
if(j == 0) {
a[id[i][j]].l = -1;
}
else {
a[id[i][j]].l = id[i][j - 1];
}
if(i == n - 1) {
a[id[i][j]].d = -1;
}
else {
a[id[i][j]].d = id[i + 1][j];
}
if(j == n - 1) {
a[id[i][j]].r = -1;
}
else {
a[id[i][j]].r = id[i][j + 1];
}
}
}
}
int b[201];
void Work1(int L, int R, int D) {
int s1 = root;
for(int i = 0; i < L; i++) {
s1 = a[s1].d;
} //old start
int s2 = s1;
for(int i = 0; i < D; i++) {
s2 = a[s2].r;
} // new start
if(L == 0) root = s2;
int s3 = s1;
for(int i = 0; i < n - 1; i++) {
s3 = a[s3].r;
} // old last
a[s1].l = s3; a[s3].r = s1;
if(L > 0) {
int s = a[s1].u, t = s2;
for(int i = 0; i < n; i++) {
a[s].d = t; a[t].u = s;
s = a[s].r; t = a[t].r;
}
}
for(int i = L + 1; i <= R; i++) {
s1 = a[s1].d; s2 = a[s2].d; s3 = a[s3].d;
a[s1].l = s3; a[s3].r = s1;
}
if(R < n - 1) {
int s = a[s1].d, t = s2;
for(int i = 0; i < n; i++) {
a[s].u = t; a[t].d = s;
s = a[s].r; t = a[t].r;
}
}
}
void Work2(int L, int R, int D) {
int s1 = root;
for(int i = 0; i < L; i++) {
s1 = a[s1].r;
} //old start
int s2 = s1;
for(int i = 0; i < D; i++) {
s2 = a[s2].d;
} // new start
if(L == 0) root = s2;
int s3 = s1;
for(int i = 0; i < n - 1; i++) {
s3 = a[s3].d;
} // old last
a[s1].u = s3; a[s3].d = s1;
if(L > 0) {
int s = a[s1].l, t = s2;
for(int i = 0; i < n; i++) {
a[s].r = t; a[t].l = s;
s = a[s].d; t = a[t].d;
}
}
for(int i = L + 1; i <= R; i++) {
s1 = a[s1].r; s2 = a[s2].r; s3 = a[s3].r;
a[s1].u = s3; a[s3].d = s1;
}
if(R < n - 1) {
int s = a[s1].r, t = s2;
for(int i = 0; i < n; i++) {
a[t].r = s; a[s].l = t;
s = a[s].d; t = a[t].d;
}
}
}
void OutPut() {
for(int i = 0; i < n; i++) {
int s = root;
for(int j = 0; j < n; j++) {
if(j > 0) printf(" ");
printf("%d", a[root].v);
root = a[root].r;
}
root = a[s].d;
printf("\n");
}
}
int main()
{
while(scanf("%d%d", &n, &q) != EOF) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
id[i][j] = i * n + j;
a[id[i][j]].v = id[i][j];
}
}
Init(); root = id[0][0];
while(q--) {
int op, L, R, D;
scanf("%d%d%d%d", &op, &L, &R, &D);
if(op == 1) {
Work1(L, R, D);
}
else {
Work2(L, R, D);
}
}
OutPut();
}
return 0;
}