时间:08_18
- 1011 NOI2024 31.80% (703/2211)
- 1008 SunBian 55.02% (669/1216)
- 1009 不基本子串结构 20.57% (589/2864)
- 1002 scenery 21.00% (368/1752)
1011 NOI2024
思路
题目问的是“是否一定”,考虑最差情况,比自己排名高的全部拿分了,剩下的人一分不拿,与自己并列排名
最后每场比赛在自己排名之前的还是在自己排名前
尽量使每场比赛比自己强的人不同
代码
#define rep(i, j, k) for (int i = (j); i < (k); i++)
signed main() {
IOS;
int t, n, m, k, hcnt = 0, temp, a;
cin >> t;
while (t--) {
hcnt = 0;
cin >> n >> m >> k;
rep(i, 0, n) {
cin >> a;
hcnt += a - 1;
}
rep(i, 0, n) cin >> temp;
if (hcnt >= m) hcnt = m - 1;
if (hcnt + 1 <= k) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
return 0;
}
1008 SunBian
思路
一开始是个环,那么Alice操作,把环断开,变成一条线
现在游戏有点像一个下棋问题(忘记具体叫什么了)
在 \(n×m\) 的棋盘上下棋,轮流下棋(还是放硬币来着),谁先放不下就输,让你判断自己先手能赢还是后手能赢
本题策略就是,在环断开后,再将线从中间断开,接下来相当于一人一条链,跟对方的处理一样能保证自己赢
也就是,A把环变链,B把链对等切开,然后B能稳赢
\(k=1\) 或 \(k>=n\) 特判一下
代码
signed main() {
IOS;
long long t, k, n;
cin >> t;
while (t--) {
cin >> n >> k;
if (k == 1)
if (n % 2 == 1)cout << "A";
else cout << "B";
else if (k >= n)cout << "A";
else cout << "B";
}
return 0;
}
1009 不基本子串结构
思路
分类
- A是B的子串
- A只在B中出现一次,答案是B的长度
- A在B中出现不止一次,答案是-1
- 两者没有子串关系
- 取最长的公共的A的后缀和B的前缀或是A的前缀和B的后缀
- 例:ABBAB和BBABAA,那么最长的满足要求的串就是BBAB
- 则构造出的最长串为 A-BBAB-AA
- 取最长的公共的A的后缀和B的前缀或是A的前缀和B的后缀
取前缀或后缀用KMP或者哈希,题解表示哈希方法不能用ull自然溢出
代码
#include <bits/stdc++.h>
using namespace std;
#define YES "Yes"
#define NO "No"
#define F first
#define S second
#define int long long
#define ull unsigned long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (int i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
#define pii pair<int, int>
int MOD = 100000037;
int p = 100291;
ull ksm(ull a, ull b) {
ull res = 1;
while (b) {
if (b & 1) res = (res * a) % MOD;
a = (a * a) % MOD;
b >>= 1;
}
return res;
}
int solve(string &s1, string &s2) {
int n, m;
n = s1.length();
m = s2.length();
if (n > m) { // 让s1为短的,s2为长的
swap(n, m);
swap(s1, s2);
}
// 检查s1是否是s2的子串
if (s2.find(s1) != string::npos) {
int sonindex = s2.find(s1);
// 如果是子串,再检查剩余的是否有子串
if (s2.substr(sonindex + 1, m - sonindex - 1).find(s1) != string::npos)
return -1;
else
return m;
} else { // 查s1的后缀和s2的前缀的最长相等串的长度以及s1的前缀和s2的后缀的最长相等串的长度,采用哈希法
int l1 = 0, l2 = 0;
ull h1 = 0, h2 = 0;
for (int i = 0; i < n; i++) {
(h1 += (s1[n - i - 1]) * ksm(p, i)) %= MOD;
h2 = (h2 * p + s2[i]) % MOD;
if (h1 == h2) l1 = i + 1;
}
h1 = 0, h2 = 0;
for (int i = 0; i < n; i++) {
h1 = (h1 * p + s1[i]) % MOD;
(h2 += (s2[m - i - 1]) * ksm(p, i)) %= MOD;
if (h1 == h2) l2 = i + 1;
}
return min(n + m - l1, n + m - l2);
}
}
signed main() {
IOS;
int t, n, m;
string s1, s2;
cin >> t;
while (t--) {
cin >> s1 >> s2;
cout << solve(s1, s2) << endl;
}
return 0;
}
1002 scenery
思路
题目给的数据是一个类似漏斗的形状,很容易能想到想要拍完所有景色,就尽量紧凑的选取时间段拍摄
一开始想的是从漏斗下面开始填充,称为第一层,那么第二层就是紧贴着第一层选择的时间段进行拍摄,要么从上往下,先把 \([l_i,r_i]\) 的左右填充了,往下层转移
不过没想到怎么转移,根据jls和题解的方式,\(dp[i][j]\) 表示 第 \(i\) 层的 \(j\) 时间到 \(dp[i][j]\) 是没被选择的
那么每次转移大致就是, \(j+t[i]\) 到 \(dp[i-1][j]\) 和 \(j\) 到 \(dp[i-1][j]-t[i]\)
也就是 \(dp[i][j]=dp[i-1][j]-t[i],dp[i][j+t[i]]=dp[i-1][j]\)
然后考虑一下左右边界
代码
代码抄借鉴了jls的写法
#define rep(i, j, k) for (int i = (j); i < (k); i++)
const int N = 5010;
int l[N], r[N], t[N];
void solve() {
int n, m;
cin >> n >> m;
rep(i, 0, n) cin >> l[i] >> r[i] >> t[i];
vector<int> dp(m + 1, -1);
dp[1] = m;
for (int i = n - 1; i >= 0; i--) {
vector<int> ndp(m + 1, -1);
for (int j = 1; j <= m; j++) {
if (dp[j] >= j) {
int L = max(l[i], j);
int R = min(r[i], dp[j]);
if (R - L + 1 >= t[i]) {
ndp[L] = max(ndp[L], R - t[i]);// 取最优
ndp[L + t[i]] = max(ndp[L + t[i]], R);
}
}
}
dp = ndp;
}
for (int i = 1; i <= m; i++)
if (dp[i] >= i - 1) return cout << "YES\n", void();
cout << "NO\n";
}
标签:钉耙,10,int,s2,s1,cin,2024,dp,define
From: https://www.cnblogs.com/lulaalu/p/18376130