T1
Topcoder SRM 567 div1 Medium - StringGame
首先字母表定了之後兩個人一定都會把字串按字母表排序。對於這樣的兩個字串,只需要數出兩個串中每種字母分別有多少個,然後對著計數陣列按字母表順序比過去,在第一個不一樣處更大的字典序小。因此先數一數每個字串中每個字元出現的次數,把出現次數列成一張 \(n\) 行 \(26\) 列的表,然後問題相當於把每列視為整體,要你把所有列重排,要求某行嚴格最小(這裡認為兩行第一個不一樣的列上的數大的行更小)。列舉每一行,依次決定把把哪一列扔到最前面去。第一次肯定要找一列使得別的行在這一列上的數都不大於當前行。把這一列扔到前面去了之後,我們可以刪去這一列上的數小於當前行的行,然後重複這個過程。如果某次找不到合法列了,則這一行不可能成為最優行。如果到最後還有行沒被刪掉,那這一行也不可能成為最優行。否則就可以。
程式碼
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
int n;
bool vis[55];
int cnt[55][30];
vector<int> vec, tmp;
bool chs[30];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
string str;
cin >> str;
for (int j = 0; j < (int)str.size(); j++) cnt[i][str[j] - 'a']++;
}
for (int i = 1; i <= n; i++) {
bool ok = 1;
// cout << i << " i\n";
vec.clear();
for (int j = 1; j <= n; j++) {
if (j != i)
vec.emplace_back(j), vis[j] = 0;
}
memset(chs, 0, sizeof chs);
for (int asdf = 0; asdf < 26; asdf++) {
int lg = -1;
for (int j = 0; j < 26; j++) {
if (chs[j])
continue;
bool tmp = 1;
for (auto v : vec) tmp &= (cnt[v][j] <= cnt[i][j]);
if (tmp)
lg = j;
}
if (lg == -1) {
ok = 0;
break;
}
// cout << lg << " lg\n";
chs[lg] = 1;
for (auto v : vec) cnt[v][lg] < cnt[i][lg] ? void() : tmp.emplace_back(v);
vec.clear();
swap(vec, tmp);
}
ok &= vec.empty();
if (ok)
cout << i - 1 << " ";
}
cout << "\n";
return 0;
}
T2
Topcoder SRM 594 div1 Medium - FoxAndGo3
發現最終情況下白子和一個與他相鄰的空格不會同時為空。所以在兩者之間連邊,發現是二分圖。可以直接做二分圖最大獨立集。
程式碼
#include <iostream>
#include <string.h>
#include <queue>
#define int long long
using namespace std;
string str[55];
const int inf = 0x7ffffffffffffff;
int S = 2505, T = 2506;
int head[25005], nxt[100005], to[100005], res[100005], ecnt = 1;
void add(int u, int v, int ww) {
to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, res[ecnt] = ww;
to[++ecnt] = u, nxt[ecnt] = head[v], head[v] = ecnt, res[ecnt] = 0;
}
queue<int> q;
int dep[25005];
int cur[25005];
bool bfs() {
q.push(S);
memset(dep, 0, sizeof dep);
dep[S] = 1;
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if (!dep[v] && res[i] > 0) {
dep[v] = dep[x] + 1;
q.push(v);
}
}
}
return (dep[T] > 0);
}
int dfs(int x, int flow) {
if (!flow)
return 0;
if (x == T)
return flow;
int ret = 0;
for (int& i = cur[x]; i; i = nxt[i]) {
int v = to[i];
if (dep[v] == dep[x] + 1 && res[i]) {
int tmp = dfs(v, min(flow, res[i]));
res[i] -= tmp;
res[i ^ 1] += tmp;
flow -= tmp;
ret += tmp;
}
}
if (!ret)
dep[x] = 0;
return ret;
}
int Dinic() {
int ret = 0;
while (bfs()) {
for (int i = 1; i <= T; i++) cur[i] = head[i];
ret += dfs(S, inf);
}
return ret;
}
int n;
inline int f(int x, int y) { return (x - 1) * n + y; }
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> str[i];
str[i] = ' ' + str[i];
}
int t = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (str[i][j] == 'o') {
str[i - 1][j] == '.' ? add(f(i - 1, j), f(i, j), inf) : void();
str[i + 1][j] == '.' ? add(f(i + 1, j), f(i, j), inf) : void();
str[i][j - 1] == '.' ? add(f(i, j - 1), f(i, j), inf) : void();
str[i][j + 1] == '.' ? add(f(i, j + 1), f(i, j), inf) : void();
add(f(i, j), T, 1);
++t;
} else if (str[i][j] == '.') {
add(S, f(i, j), 1);
++t;
}
}
}
cout << t - Dinic() << "\n";
return 0;
}
T3
Topcoder SRM 521 div1 Hard - Chimney
發現填某一行時最多隻有最上面三行是不完整的。所以可以大力分討,合併等價狀態,然後可以得到一個 \(9\) 個狀態的 dp。然後將行內轉移消去(即 如果 \(i \rightarrow j\),\(j \rightarrow k\),就直接 \(i \rightarrow k\)),發現這樣只有四個狀態會被轉移到。所以只留這四個狀態,就得到了一個 \(4\) 個狀態的 dp。然後就可以直接矩乘了。由於最後答案所在的狀態已經被刪了,所以還要根據行內轉移推出來。
程式碼
#include <iostream>
#define int long long
using namespace std;
const int P = 1000000007;
struct Matrix {
int a[4][4];
int* operator[](int x) { return a[x]; }
} I, E, T;
int t[4][4] = {
{ 2, 6, 32, 64 },
{ 2, 6, 32, 64 },
{ 1, 3, 16, 32 },
{ 0, 2, 12, 24 }
};
Matrix operator*(Matrix a, Matrix b) {
Matrix c;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
c[i][j] = 0;
for (int k = 0; k < 4; k++)
c[i][j] = (c[i][j] + a[i][k] * b[k][j] % P) % P;
}
}
return c;
}
Matrix qpow(Matrix x, int y) {
Matrix ret = E;
while (y) {
if (y & 1)
ret = ret * x;
y >>= 1;
x = x * x;
}
return ret;
}
int n;
signed main() {
cin >> n;
if (n == 1) {
cout << 24 << "\n";
return 0;
}
I[0][3] = 4;
for (int i = 0; i < 4; i++) {
E[i][i] = 1;
for (int j = 0; j < 4; j++)
T[i][j] = t[i][j];
}
I = I * qpow(T, n - 1);
cout << (16 * I[0][0] + 16 * I[0][1] + 8 * I[0][2] + 6 * I[0][3]) % P << "\n";
return 0;
}
T4
Topcoder SRM 431 div1 Hard - PerfectRectangles
怎麼 d1t3 還放這種水題(
有一個很典的套路:列舉行,行上每個元素的值是這個元素往上到第一個不可選位置的距離,然後兩遍單調棧求出每個位置往左往右最遠到什麼地方。注意到這樣能夠保證上左右邊界極大,但是不保證下邊界。所以只需要對每個位置再看它支配的區間在下面一行有沒有不可選位置。如果有的話就計入答案,否則不計。
程式碼
#include <iostream>
#include <map>
#define int long long
using namespace std;
int n, m;
pair<int, int> stk[2005];
int sz;
int ans = 0;
int tol[2005], tor[2005];
bool a[2005][2005];
int up[2005][2005];
int X[4000005], Y[4000005];
int s[2005][2005];
map<pair<int, int>, int> mp[2005];
signed main() {
cin >> n >> m;
int X0, A, B, C, D, Y0;
cin >> X0 >> A >> B >> Y0 >> C >> D;
X[0] = X0 % n;
Y[0] = Y0 % n;
a[X[0] + 1][Y[0] + 1] = 1;
for (int i = 1; i < m; i++) {
X[i] = (X[i-1]*A+B) % n;
Y[i] = (Y[i-1]*C+D) % n;
a[X[i] + 1][Y[i] + 1] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
up[i][j] = (a[i][j] ? i : up[i - 1][j]);
s[i][j] = s[i][j - 1] + a[i][j];
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
stk[sz = 0] = make_pair(0, 0);
for (int j = 1; j <= n; j++) {
while (sz && i - up[i][j] <= stk[sz].first) --sz;
tol[j] = stk[sz].second + 1;
stk[++sz] = make_pair(i - up[i][j], j);
}
stk[sz = 0] = make_pair(0, n + 1);
for (int j = n; j; j--) {
while (sz && i - up[i][j] <= stk[sz].first) --sz;
tor[j] = stk[sz].second - 1;
stk[++sz] = make_pair(i - up[i][j], j);
}
for (int j = 1; j <= n; j++) mp[j].clear();
for (int j = 1; j <= n; j++) {
if (up[i][j] == i)
continue;
if (i == n || s[i + 1][tor[j]] != s[i + 1][tol[j] - 1]) {
if (mp[up[i][j]][make_pair(tol[j], tor[j])] == 0) {
mp[up[i][j]][make_pair(tol[j], tor[j])] = 1;
++ans;
}
}
}
}
cout << ans << "\n";
return 0;
}
T5
Topcoder SRM 437 div1 Hard - TheSum
後面再說。
程式碼
說了後面再說。
觀察性質。
大力分討。
不要亂改題目給的資料生成器!!!