# 29-30 考试总结

### T1 ples

#### 题目大意

$$N$$个男生和$$N$$个女生参加舞会，跳舞时只能是一个男生一个女生，我们知道每个人的身高，同时某个男生只想和比他高（或者矮）的女生跳（有人跟你跳还挑三拣四的？），女生也是一样的，身高一样的人都不想与对方跳。给出所有数据，输出满足人们希望的前提下组成的最多对数。

#### 代码

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
const int BASE = 1500;
const int INF = 0x3f3f3f3f;
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("ples.in", "r", stdin);
freopen("ples.out", "w", stdout);
}
int n, s, t, maxflow;
struct Node{ int l, r; }A[1005], B[1005];
inline int min(int a, int b) { return a < b ? a : b; }
struct Sakura { int to, nxt, w; }sak[2200005]; int head[4010], cnt = 1;
inline void add(int x, int y, int w) {
++cnt;
sak[cnt].to = y, sak[cnt].w = w, sak[cnt].nxt = head[x], head[x] = cnt;
++cnt;
sak[cnt].to = x, sak[cnt].w = 0, sak[cnt].nxt = head[y], head[y] = cnt;
//	printf("%d -> %d : %d\n", x, y, w);
}

int dep[4010];
inline bool bfs() {
std :: queue<int> q;
memset(dep, 0, sizeof dep);
dep[s] = 1, q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u];i;i =sak[i].nxt) {
int v = sak[i].to, w = sak[i].w;
if (!dep[v] && w) {
dep[v] = dep[u] + 1;
if (v == t) return 1;
q.push(v);
}
}
}
return 0;
}

inline int dfs(int u, int flow) {
if (u == t) return flow;
int rest = flow, rlow;
for (int i = head[u];i && rest;i = sak[i].nxt) {
int v = sak[i].to, w = sak[i].w;
if (w && dep[v] == dep[u] + 1) {
rlow = dfs(v, min(rest, w));
if (!rlow) dep[v] = 0;
sak[i].w -= rlow;
sak[i ^ 1].w += rlow;
rest -= rlow;
}
}
return flow - rest;
}

inline int Dinic() {
int lowflow;
while (bfs()) {
while(lowflow = dfs(s, INF)) maxflow += lowflow;
}
return maxflow;
}

int main(){
hand_in();
s = 4004, t = 4005;
for (int i = 1, x;i <= n; ++i) {
if (x < 0) A[- x - BASE].l ++;
else A[x - BASE].r ++;
}

for (int i = 1, x;i <= n; ++i) {
if (x < 0) B[- x - BASE].l ++;
else B[x - BASE].r ++;
}

/* A.l 0~1000 A.r 1001~2001 B.l 2002~3002  B.r 3003~4003 */

/* S --- A.l / A.r 连源点 */
for (int i = 0;i <= 1000; ++i) {
if (A[i].r) add(s, i + 1001, A[i].r);
}

/* A.l --- B.r 男高女矮 */
for (int i = 1;i <= 1000; ++i) {
for (int j = 0;j < i; ++j) {
if (A[i].l && B[j].r) add(i, j + 3003, INF);
}
}

/* A.r --- B.l 男矮女高 */
for (int i = 0;i < 1000; ++i) {
for (int j = i + 1;j <= 1000; ++j) {
if (A[i].r && B[j].l) add(i + 1001, j + 2002, INF);
}
}

/* B.l / B.r --- T 连汇点 */
for (int j = 0;j <= 1000; ++j) {
if (B[j].l) add(j + 2002, t, B[j].l);
if (B[j].r) add(j + 3003, t, B[j].r);
}

printf("%d", Dinic());
return 0;
}


### T2 sort

#### 代码

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ll long long
const int N = 100000 + 5;
using std :: reverse;
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
}
int n, a[N];
ll ans;
inline bool judge() {
for (int i = 2;i <= n; ++i) {
if (a[i] < a[i - 1]) return 1;
}
return 0;
}
int c[N];
inline int lowbit(int x) {
return x & (-x);
}
for (int i = x;i <= n; i += lowbit(i)) c[i] ++;
}
int res = 0;
if (!x) return 0;
for (int i = x;i;i -= lowbit(i)) res += c[i];
return res;
}
inline void solve() {
int st = 1, ed = 1, len = 1;
for (int i = 2;i <= n; ++i) {
if (a[i] < a[i - 1]) {
ed = i, len ++;
}
else {
if (len > 1) {
reverse(a + st, a + ed + 1);
ans ++;
}
st = i, ed = i, len = 1;
}
}
if (len > 1) {
reverse(a + st, a + ed + 1);
ans ++;
}
for (int i = n;i >= 1; --i) {
}
}
int main(){
hand_in();
for (int i = 1;i <= n; ++i) a[i] = read();
solve();
printf("%lld", ans);
return 0;
}


### T3 skakac(To be continue)

#### 分析

##### 30~50pt做法

$$f[i][j][k]$$表示当前时间为$$i$$，棋盘位置为$$(j,k)$$是否到达的状态，然后就可以枚举$$i-1$$的状态转移过来，用个滚动数组优化下，空间复杂度$$O(n^2)$$，时间复杂度$$O(Tn^2)$$

##### 60~100pt做法

$$g[i][j][k]$$表示第k行能整除$$p_i^j$$时的情况，那么把$$T$$也分解质因数，棋盘的情况就可以表示为$$g[1][c_1] \& g[2][c_2] ... g[k][c_k]$$

#### 代码

#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define Re register
const int P = 1000000007;
const int BASE = 1e6;
const int N = 30 + 5;
using std :: vector;
using std :: sort;
inline void hand_in() {
freopen("skakac.in", "r", stdin);
freopen("skakac.out", "w", stdout);
}
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
int n, t, st_x, st_y;
int mp[N][N];

struct Node {
int t, x, y;
Node (int a = 0, int b = 0, int c = 0) : t(a), x(b), y(c) {}
friend bool operator < (Node a, Node b) { return a.t < b.t; }
}ans[N * N], rate[BASE + 5]; int oo, pp;

/* 预处理质数 */
int prim[BASE + 5], vis[BASE + 5], tot, ps;
inline void init() {
for (Re int i = 2;i <= BASE; ++i) {
if (!vis[i]) {
prim[++tot] = i, vis[i] = tot;
if (i <= 1000) ps = tot; /* 小优化:记录下1000以内的质数到了哪里 */
}
for (Re int j = 1;j <= tot; ++j) {
if ((ll)i * (ll)prim[j] > BASE) break;
vis[i * prim[j]] = j;
if (i % prim[j] == 0) break;
}
}
}

/* 对(x,y)上的k值进行质因数分解 */
/*
h: h[i][j][k]表示
g: g[i][j][k]表示第k行能整除pi^j时的情况
*/
int h[250][250][35], g[250][25][35];
inline void divide(int k, int x, int y) {
for (Re int i = 1;i <= ps; ++i) {
int ret = 0;
while (k % prim[i] == 0) ret ++, k /= prim[i];
/* 既然x^ret能够到达，所以高于ret的次幂都行，最多达到2^20 */
for (;ret <= 20; ++ret) g[i][ret][x] |= (1 << y);
}
}

/* 质因数分解 */
struct Divide {
int pr, nm;
friend bool operator < (Divide a, Divide b) { return a.pr < b.pr; }
}dv[BASE + 5]; int w[35];
inline void work(int x) {
int ct = 0, pos = 1;

/* 会超时？？？ */
//	for (int i = 1, ret;prim[i] <= x; ++i) {
//		if (x % prim[i]) continue;
//		ret = 0;
//		while (x % prim[i] == 0) ret ++, x /= prim[i];
//		if (ret) dv[++ct].pr = i, dv[ct].nm = ret;
//	}

while (x ^ 1) {
int ret = 0, op = vis[x];
while (x % prim[op] == 0) ret ++, x /= prim[op];
dv[++ct].pr = op, dv[ct].nm = ret;
}
sort(dv + 1, dv + 1 + ct);

for (int i = 1;i <= ct && dv[i].pr <= ps; ++i) {
for (int j = 1;j <= n; ++j) {
w[j] &= g[dv[i].pr][dv[i].nm][j];
}
if (pos < dv[i].pr) {
for (int j = 1;j <= n; ++j) {
w[j] &= h[pos][dv[i].pr - 1][j];
}
}
pos = dv[i].pr + 1;
}
if (pos < ps) for (int i = 1;i <= n; ++i) w[i] &= h[pos][ps][i];
}

int f[2][35], now, pre;
int main(){
hand_in();

/* 读入 */

/* 预处理出质数 */
init();

/* 对地图分类处理:1000以上用倍数，反之分解它 */
for (Re int i = 1;i <= n; ++i) {
for (Re int j = 1;j <= n; ++j) {
if (mp[i][j] >= 1500) {
for (Re int k = 1;k * mp[i][j] <= t; ++k) rate[++pp] = Node(k * mp[i][j], i, j);
}
else {
divide(mp[i][j], i, j);
}
}
}

/* 排序后可以依次考虑 */
sort(rate + 1, rate + 1 + pp);
for (Re int i = 1;i <= ps; ++i) {
for (Re int j = i;j <= ps; ++j) {
for (Re int s = 1;s <= n; ++s) {
/* 初始化全为1 */
h[i][j][s] = (1 << (n + 1)) - 1;
for (Re int k = i;k <= j; ++k) {
h[i][j][s] &= g[k][0][s];
}
}
}
}
f[0][st_x] |= (1 << st_y);
for (Re int i = 1, p = 1;i <= t; ++i) {
now ^= 1, pre = now ^ 1;
for (Re int j = 1;j <= n; ++j) f[now][j] = 0, w[j] = (1 << (n + 1)) - 1;
for (Re int j = 1;j <= n; ++j) {
if (j > 1) f[now][j] |= (f[pre][j - 1] >> 2) | (f[pre][j - 1] << 2);
if (j < n) f[now][j] |= (f[pre][j + 1] >> 2) | (f[pre][j + 1] << 2);
if (j > 2) f[now][j] |= (f[pre][j - 2] >> 1) | (f[pre][j - 2] << 1);
if (j < n - 1) f[now][j] |= (f[pre][j + 2] >> 1) | (f[pre][j + 2] << 1);
}
work(i);
while (p <= pp && rate[p].t == i) w[rate[p].x] |= (1 << rate[p].y), ++p;
for (Re int j = 1;j <= n; ++j) f[now][j] &= w[j];
}
for (Re int i = 1;i <= n; ++i) {
for (Re int j = 1;j <= n; ++j) {
if (f[now][i] & (1 << j)) ans[++oo].x = i, ans[oo].y = j;
}
}
printf("%d\n", oo);
for (Re int i = 1;i <= oo; ++i) {
printf("%d %d\n", ans[i].x, ans[i].y);
}
return 0;
}


### T4 kom

#### 代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ll long long
inline void hand_in() {
freopen("kom.in", "r", stdin);
freopen("kom.out", "w", stdout);
}
int n, len, v, pan[1025];
char ch[20]; ll ret;
int main(){
hand_in();
scanf("%d", &n), v = (1 << 10);
for (int i = 1, a;i <= n; ++i) {
scanf("%s", ch + 1);
len = strlen(ch + 1), a = 0;
for (int j = 1;j <= len; ++j) {
a |= (1 << (ch[j] - '0'));
}
pan[a] ++;
}
for (int i = 0;i < v; ++i) {
for (int j = 0;j < v; ++j) {
if (i & j) {
ret += (ll)pan[i] * (ll)pan[j];
if (i == j) ret -= (ll)pan[i];
}
}
}
printf("%lld\n", ret >> 1);
return 0;
}


### T5 fun

#### 代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ll long long
const int P = 1000000007;
const int N = 30;
inline void hand_in() {
freopen("fun.in", "r", stdin);
freopen("fun.out", "w", stdout);
}
inline bool is_num(char *s) { if (s[1] < '0' || s[1] > '9') return 0; return 1; }
int cnt, to[N << 1], nxt[N << 1], head[N]; inline void add(int x, int y) { ++cnt; to[cnt] = y, nxt[cnt] = head[x], head[x] = cnt; }
inline int change_num(char *s) { int l = strlen(s + 1); int res = 0; for (int i = 1;i <= l; ++i) { res = res * 10 + s[i] - '0'; } return res; }

ll dfs(int, int);
ll f[N][100005], ans = 1;
int n, l[N], r[N], rate[N];
char ls[10], rs[10];

inline ll find(int now, int lim) {
ll res = 1;
while (p) {
nt = to[p];
res *= dfs(nt, lim) % P;
p = nxt[p];
}
return res;
}

inline ll dfs(int now, int lim) {
if (~f[now][lim]) return f[now][lim];
int last = lim;
if (rate[now] == 0) {
while (last <= r[now] && f[now][last] == -1) last ++;
if (last > r[now]) f[now][last] = 0;
last --;
while (last >= lim) {
f[now][last] = (f[now][last + 1] + find(now, last)) % P;
last --;
}
}
else if (rate[now] == 1) {
while (last >= l[now] && f[now][last] == -1) last --;
if (last < l[now]) f[now][last] = 0;
last ++;
while (last <= lim) {
f[now][last] = (f[now][last - 1] + find(now, last)) % P;
last ++;
}
}
else {
f[now][lim] = 0;
for (int i = l[now];i <= r[now]; ++i) {
f[now][i] = find(now, i);
f[now][lim] = (f[now][i] + f[now][lim]) % P;
}
}
return f[now][lim];
}

int main(){
//	hand_in();
scanf("%d", &n);
memset(f, -1, sizeof f);
for (int i = 0;i < n; ++i) {
scanf("%s %s", ls + 1, rs + 1);
rate[i] = -1;
if (is_num(ls)) {
l[i] = change_num(ls);
}
else {
rate[i] = 0;
l[i] = ls[1] - 'a';
}

if (is_num(rs)) {
r[i] = change_num(rs);
}
else {
rate[i] = 1;
r[i] = rs[1] - 'a';
}
}
for (int i = 0;i < n; ++i) {
if (rate[i] == -1) {
ans = ans * dfs(i, 0) % P;
}
}
printf("%lld", ans);
return 0;
}



### T6 ras

#### 代码

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ll long long
using std :: sort;
const int N = 100000 + 5;
int f = 1, x = 0; char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f * x;
}
inline void hand_in() {
freopen("ras.in", "r", stdin);
freopen("ras.out", "w", stdout);
}
int n, c; ll ans, s;
struct Data { int l, t; }mk[200005], rsd[200005];
inline bool cmp(const Data &a, const Data &b) { return a.t < b.t; }

struct Segment_Tree {
struct Node {
int l, r;
ll x, t;
}tr[N << 2];

#define ls (p << 1)
#define rs ((p << 1) | 1)

inline void build(int p, int l, int r) {
tr[p].l = l, tr[p].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
}

inline void change(int p, int x, int a, int b) {
tr[p].x += a, tr[p].t += b;
if (tr[p].l == tr[p].r) return;
int mid = (tr[p].l + tr[p].r) >> 1;
if (x <= mid) change(ls, x, a, b);
else change(rs, x, a, b);
}

inline ll ask_x(int p, int l, int r) {
if (l <= tr[p].l && tr[p].r <= r) {
return tr[p].x;
}
int mid = (tr[p].l + tr[p].r) >> 1;
ll res = 0;
if (l <= mid) res += ask_x(ls, l, r);
if (r > mid) res += ask_x(rs, l, r);
return res;
}

inline ll ask_t(int p, int l, int r) {
if (l <= tr[p].l && tr[p].r <= r) {
return tr[p].t;
}
int mid = (tr[p].l + tr[p].r) >> 1;
ll res = 0;
if (l <= mid) res += ask_t(ls, l, r);
if (r > mid) res += ask_t(rs, l, r);
return res;
}
}st;

int main(){
hand_in();
for (int i = 1;i <= n; ++i) {
rsd[i] = mk[i], ans += mk[i].l;
}
st.build(1, 1, 100001);
sort(mk + 1, mk + 1 + n, cmp);
for (int i = 1;i <= n; ++i) {
s += mk[i].t;
ans -= s;
st.change(1, mk[i].t, mk[i].t, 1);
}
printf("%lld\n", ans);
for (int i = 1, id, l, t;i <= c; ++i) {