A. LuoTianyi and the Palindrome String
题意:
给一个回文串s(1 <= |s| <= 50),求出最长非回文子序列的长度,如果没有非回文子序列,输出-1.
思路:
如果回文串所以字母相等,输出-1,否则输出回文串长度减一即可,因为回文串删除一个字符一定可以得到非回文串
代码:
string s;
void solve() {
cin >> s;
int n = s.size();
s = " " + s;
bool ok = true;
for(int i = 1; i <= n - 1; i ++ )
if (s[i] != s[i + 1]) {
ok = false;
break;
}
if (ok) cout << -1 << endl;
else cout << n - 1 << endl;
}
B. LuoTianyi and the Table
题意:
给出n*m的空白矩阵和n*m(2 <= n, m <= 100)个数字,可以将数字填充在空白矩阵中,假设每个点的贡献为以该点为矩形右下角,(1, 1)为矩形左上角的一个矩形,其中的最大值减最小值。求所有点的贡献之和。
思路:
找到最大值和最小值,(1, 1)放最大值或者最小值可以使贡献和最大,两种情况取max即可。假设(1, 1)放最大值,那么(1, 2)和 (2, 1)放最小和第二小,当然,最小的要放在贡献最大的那个点,贡献最大的点的贡献次数为mm = max(n * (m - 1), m * (n - 1)), 那么另一个点为n*m - mm次。(1,1)放最小值同理。
代码:
int n, m;
int b[maxn];
void solve() {
cin >> n >> m;
int x;
for (int i = 1; i <= n * m; i++) cin >> b[i];
sort(b + 1, b + n * m + 1);
int ti = n * m - 1;
int mm = max(n * (m - 1), m * (n - 1));
int ans1 = mm * (b[n * m] - b[1]) + (ti - mm) * (b[n * m - 1] - b[1]);
int ans2 = mm * (b[n * m] - b[1]) + (ti - mm) * (b[n * m] - b[2]);
cout << max(ans1, ans2) << endl;
}
C. LuoTianyi and the Show
题意:
现在有n个人和m(1 <= n, m <= 1e5)个座位,和一个数组x,有下面三种操作
xi == -1, 则当前这个人坐在已经坐下的人当中最左边的人的左边,如果没有人已经坐下,则坐在m号座位上;
xi == -2,则当前这个人坐在已经坐下的人当中最右边的人的右边,如果没有人已经坐下,则坐在1号座位上;
xi > 0,则坐在xi号座位上。
如果座位上有人了,就不能再坐了。
求最多能坐下多少人。
思路:
总共只有三种人:-1,-2和正整数。分三种情况考虑,-1先坐,-2先坐和正整数先坐。设n1为-1的个数,n2为-2的个数,s.size()为正整数的个数,因为同一个位置不能坐两个人,所以要用set去重。如果先坐-1,ans1 = min(m, n1 + s.size()), 先坐-2, ans2 = min(m, n2 + s.size()),如果先坐正整数的话,得考虑从哪个人开始向左右扩张,枚举一下正整数的人即可。
代码:
int n, m;
void solve() {
cin >> n >> m;
int x;
int n1 = 0, n2 = 0;
set<int> s;
for (int i = 1; i <= n; i++) {
cin >> x;
if (x == -1) n1++;
else if (x == -2) n2++;
else s.insert(x);
}
int ans1 = min(m, n1 + s.size());
int ans2 = min(m, n2 + s.size());
int ans3 = 0;
int al = 0;
for (auto p : s) {
ans3 = max(ans3, min(m, min(p - 1 - al, n1) + min(n2, m - p - (s.size() - al - 1)) + s.size()));
al++;
}
cout << min(m, max({ ans1, ans2, ans3 })) << endl;
}
D2. LuoTianyi and the Floating Islands (Hard Version)
题意:
给一个含有n个节点的树和k个人(1 <= k <= n <= 2e5),k个人可以随机站在树上的k个节点,现在规定good点:到k个点的距离之和最小。求good点 mod 1e9 + 7的期望。
思路:
选择一个点,如果有一个分支上站的人大于k/2,那么往这个分支上移动距离之和一定会变小,不符合题意。所以通过good作一条边,两边的人数是相等的,因此只要找到good边的期望,最后答案加一就是good点的期望。如果k是奇数,那么good点一定是在中间,答案是1。如果k是偶数,枚举每一个点,假设sz[x]为x的子树的大小,让ans += C(sz[x], k/2)* C(n - sz[x], k/2)。
代码:
int qmi(ll a, ll b, ll p) { ll ans = 1; while (b) { if (b & 1) ans = ans * a % p; a = a * a % p; b >>= 1; } return ans; }
int n, k;
int sz[maxn];
int fact[maxn], infact[maxn];
void init() {
fact[0] = infact[0] = 1;
for (int i = 1; i < maxn; i++)
{
fact[i] = fact[i - 1] * i % mod;
infact[i] = infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
}
int C(int a, int b) {
if (a < 0 || b < 0 || a < b) return 0;
return fact[a] % mod * infact[b] % mod * infact[a - b] % mod;
}
vector<int> g[maxn];
int ans = 0;
void dfs(int u, int fa) {
sz[u] = 1;
for (auto i : g[u]) {
if (i == fa) continue;
dfs(i, u);
sz[u] += sz[i];
}
ans = (ans + C(sz[u], k / 2) * C(n - sz[u], k / 2) % mod) % mod;
}
void solve() {
cin >> n >> k;
for (int i = 1; i <= n - 1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
if (k & 1) {
cout << 1 << endl;
return;
}
dfs(1, -1);
cout << 1 + ans * qmi(C(n, k), mod - 2, mod) % mod << endl;
}
signed main() {
IOS;
int t = 1;
//cin >> t;
init();
while (t--) solve();
}