因为题目要求最多能修改 n * m / 2 向下取整次,我们可以判断 A 和 B 之间有几个不同的位置并统计个数,最终如果不同的个数不超过 n * m / 2,那就可以直接修改。超过了就输出 A 的反图,这样可以证明答案是正确的。可以多举几个例子验证一下,这里不进行严格证明。比赛的时候就要大胆猜结论,同时也要细心考虑到特殊情况。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n, m;
int cnt_a;
int cnt_b;
int cnt;
char a[1010][1010];
char b[1010][1010];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cin >> a[i][j];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cin >> b[i][j];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(a[i][j] != b[i][j]) cnt++;
}
}
if(cnt <= n * m / 2){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cout << a[i][j];
}
cout << '\n';
}
}
else{
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(a[i][j] == '.') cout << "X";
else cout << ".";
}
cout << '\n';
}
}
}
分四种情况,第一种让 A 走完全程,第二种让 B 走完全程,第三种让 A 向右走到头 B 向左走到头,前三种都直接算就可以了。第四种就有些麻烦了,在考虑完之前的情况后,只剩下在 A 和 B 之间有一个分界点,左边让 A 走,右边让 B 走,这种情况了。由于中间的位置很多,我们要利用二分来找到最优的位置(或者三分也可以,这应该是个单峰函数)。每次找到的位置我们都可以计算出左边所用的时间和右边所用的时间,如果左边比右边大,说明左边走的慢可以往左移动一些,反之就是往右。最后注意点精度问题就可以了。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int t;
double n, p1, v1, v2, p2;
double ans_1, ans_2, ans_3, ans;
int check(double x){
double t1 = (min(x - p1, p1) + x) / v1;
double t2 = (min(n - p2, p2 - x) + n - x) / v2;
ans = min(ans , max(t1, t2));
if(abs(t1 - t2) <= 0.0000001) return 0;
if(t1 - t2 < 0) return -1;
return 1;
}
int main(){
cin >> t;
while(t--){
cin >> n >> p1 >> v1 >> p2 >> v2;
if(p1 > p2){
swap(p1, p2);
swap(v1, v2);
}
ans_1 = (min(p1, n - p1) + n) / v1;
ans_2 = (min(p2, n - p2) + n) / v2;
ans_3 = max((n - p1) / v1, p2 / v2);
ans = min(ans_1, min(ans_2, ans_3));
double l = p1;
double r = p2;
while(r - l >= 0.0000001){
double mid = (l + r) / 2;
if(check(mid) < 0) l = mid;
else r = mid;
}
printf("%.10lf\n", ans);
}
system("pause");
}
这道题是个 dp,我们设 dp[n] 表示在 k 的情况下的答案,那么考虑 n 个数中最小的那个数的放置情况,显然只能放在前 k 个数中,于是枚举这个数的位置。那么更新方程为:
可以进一步推导一下
这里还要用前缀和优化一下,因为有除数取模,别忘了取逆元。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int mod = 998244353;
const int maxn = 10000100;
int n, k;
int dp[maxn];
int sum[maxn];
int fac[maxn];
int fav[maxn];
int q_pow(int a, int b, int p){
int cnt = 1;
while(b){
if(b & 1) cnt = 1ll * cnt * a % mod;
b >>= 1;
a = 1ll * a * a % mod;
}
return cnt;
}
int main(){
cin >> n >> k;
dp[0] = dp[1] = 1;
sum[1] = 1;
sum[2] = 2;
fac[0] = 1;
for(int i = 1; i <= maxn; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
fav[maxn - 1] = q_pow(fac[maxn - 1], mod - 2, mod);
for(int i = maxn - 2; i >= 0; i--) fav[i] = 1ll * fav[i + 1] * (i + 1) % mod;
for(int i = 2; i <= n; i++){
dp[i] = 1ll * fac[i - 1] * (sum[i] - sum[max(0, i - k)]) % mod;
sum[i + 1] = (sum[i] + 1ll * dp[i] * fav[i] % mod) %mod;
}
dp[n] = (dp[n] + mod) % mod;
cout << dp[n] << '\n';
system("pause");
}
应该是本场签到题,找规律。因为斐波那契数列的构成形式,数字从第一个开始就是:奇奇偶奇奇偶奇奇偶...所以循环节就是 3。我们假设 cnt 表示有多少个完整的循环节,siz 就是多余的奇数的个数。(2 * cnt + siz) * cnt 就是奇数 * 偶数的个数,cnt * (cnt - 1) / 2 就是偶数 * 偶数的个数。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
int n;
int ans;
signed main(){
cin >> n;
int cnt = n / 3;
int siz = n % 3;
ans = cnt * (cnt - 1) / 2 + (2 * cnt + siz) * cnt;
cout << ans << endl;
}
题目要我们找到 n 个以原点为圆心的同心圆,被 m 条过原点的直线切割(平分同心圆),使上面的所以交点距离之和最小。
因为是总的和,所以我们没必要具体统计各个点之间的具体距离,直接一起考虑就可以了。
在圆上的每个点与原点的距离和为 2 * m * (r1 + r2)
在不同圆上的两个点的距离和为 2 * m * (r2 - r1)
在圆上的不同点之间的距离可以分为两类
1. 经过圆弧
2. 经过两次半径
处理完上述步骤后,再计算两圆上的 dis(pi, qj),其实就是求 dis(pi, pj) + dis(pj, qj)
最后还要考虑一种特殊情况 m = 1,要把答案减去 n * (n + 1) / 2 * 2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1010;
const double pi = 3.141592654;
int n, m, k;
double ans;
double dis[maxn];
double cal(int r1, int r2){
return ((2 * m - 2 * k - 1) * 2 * r1 + pi / m * k * (k + 1) * r1 + (2 * m - 1) * (r2 - r1)) * 2 * m + 2 * m * (r2 - r1);
}
int main(){
cin >> n >> m;
k = 2 * m / pi;
for(int i = 1; i <= n; i++){
dis[i] = 1.0 * (2 * m - 2 * k - 1) * i * 2 * m + pi * k * (k + 1) * i + 2 * m * i;
ans += dis[i];
}
for(int i = 1; i < n; i++)
for(int j = i + 1; j <= n; j++)
ans += cal(i, j);
if(m == 1) ans -= n * (n + 1);
printf("%.10lf\n", ans);
}
暴力模拟就可以。我们分别从前往后统计 n 个文件路径和 m 个不能删除的文件路径。然后在 n 个里面找能删的地方。如果不能删或者删过就直接略过可以了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
#define int long long
int n, m, t;
string a, b;
string str;
map<string,int> mp;
map<string,int> vis;
vector<string> s[1005];
signed main(){
cin >> t;
while(t--){
vis.clear();
mp.clear();
cin >> n >> m;
for(int i = 1; i <= n; i++) s[i].clear();
for(int i = 1; i <= n; i++){
cin >> a;
int len = a.size();
str = "";
for(int j = 0; j < len; j++){
if(a[j] != '/') str += a[j];
else{
str += '/';
s[i].push_back(str);
}
if(j == len - 1) s[i].push_back(str);
}
}
for(int i = 1; i <= m; i++){
cin >> a;
int len = a.size();
str = "";
for(int j = 0; j < len; j++){
if(a[j] != '/') str += a[j];
else{
str += '/';
mp[str] = 1;
}
if(j == len - 1) mp[str] = 1;
}
}
int ans = 0;
for(int i = 1; i <= n; i++){
for(int j = 0; j < s[i].size(); j++){
if(vis[s[i][j]]) break;
else{
if(mp[s[i][j]]) continue;
else{
vis[s[i][j]] = 1;
ans++;
break;
}
}
}
}
cout << ans << '\n';
}
}