- define时间:
#define itn int
#define int long long
#define ind long double
#define yes cout << "Yes"
#define no cout << "No"
#define pii pair<long long, long long>
#define pci pair<char, int>
#define re return;
L.easy
签到,手玩样例容易发现按照题目要求随便取数即可。
简单地考虑,第一行为 1+2=31+2=31+2=3,除去最后一行的每行在这基础上加上 (n+1)∗2(n+1)*2(n+1)∗2,最后一行加上首尾两个数即可。
void solve(){
int ans = 3;
int t = 3;
for (int i = 2; i < n; i++)
{
t += (n + 1) * 2;
ans += t;
}
ans += n * n + (n * n - n + 1);
}
某位cf1700分选手一激动在这题卡了12分钟,警示大家卡题不要激动,冷静找出bug。
G.学生
思维题,首先容易发现最左端和最右端一定无法删除。
其次,如果最大值和最小值在中间也无法删除。
所以 ansansans 即为 2+2+2+ 不在左右端的最大最小值个数。
void solve(){
maxn = *max_element(a + 1, a + 1 + n);
minn = *min_element(a + 1, a + 1 + n);
if (a[1] != maxn && a[n] != maxn)
{
ans++;
}
if (a[1] != minn && a[n] != minn)
{
ans++;
}
cout << ans;
}
注意 n 的范围,最小为 1 。所以需要特判。
if (n == 1)
{
cout << 1;
re;
}
C.最大公因数
题意:问是否存在一个数和 bbb 不互质,和 aaa 互质。
我们从 bbb 往下找,记作 b1b1b1,每次除以 gcd(a,b)gcd(a,b)gcd(a,b),直到 gcd(a,b)=1gcd(a,b)=1gcd(a,b)=1,意味着此时的 b1b1b1 与 aaa 互质。
如果此时 gcd(b1,b)!=1gcd(b1,b)!=1gcd(b1,b)!=1,则有解;反之无解。
void solve(){
int a, b;
cin >> a >> b;
int b1 = b;
while (gcd(b1, a) != 1)
{
b1 /= gcd(b1, a);
}
cout << (gcd(b1, b) != 1 ? b1 : -1) << '\n';
}
J.赢
题意:问最多加 kkk 个字符,能组成多少个 lose。
可以发现添加字符能组成 lose 的字符串是有限的,不妨用 mapmapmap 存储记录一下各种串的个数,放进堆里,枚举即可得到答案。
需要注意:多余的 kkk 可以通过每次取 444 组成 lose。
void solve()
{
map<string, int> mp;//map存储各种串组成lose需要的k的个数
mp["lose"] = 0;
mp["los"] = mp["loe"] = mp["ose"] = mp["lse"] = 1;
mp["lo"] = mp["ls"] = mp["le"] = mp["os"] = mp["oe"] = mp["se"] = 2;
mp["l"] = mp["o"] = mp["s"] = mp["e"] = 3;
cin >> k >> s1;
n = s1.size();
priority_queue<int, vector<int>, greater<int>> pq;//vp时堆开反wa了一发
string ss = "";
ans = 0;
for (int i = 0; i < n; i++)
{
string tp = ss + s1[i];
if (mp.find(tp) != mp.end())
{
ss = tp;
}
else
{
if (mp.find(ss) != mp.end())
pq.push(mp[ss]);
ss = "";
ss += s1[i];
}
}
if (mp.find(ss) != mp.end())
pq.push(mp[ss]);
while (pq.size())
{
if (k >= pq.top())
{
ans++;
k -= pq.top();
pq.pop();
}
else
{
break;
}
}
ans += k / 4;
cout << ans;
}
D. 买股票
题意:给一个初始值和n个 +++ 或 ∗*∗ 的操作,求结果最大值。
贪心地考虑,先 +++ 或 ∗*∗ 一个较大的数,对后续的增长更有利。
观察到 n<=30n<=30n<=30,所以想到先将两种操作分开,按从大到小排序,然后 dfs 搜索找最大值即可。
#define ind double
void solve()
{
ind v;
cin >> n >> v;
m = n;
vector<ind> plus, mul;
ind x;
while (m--)
{
cin >> ch1 >> x;
if (ch1 == '+')
{
plus.emplace_back(x);
}
else
{
mul.emplace_back(x);
}
}
sort(plus.begin(), plus.end(), greater<>());
sort(mul.begin(), mul.end(), greater<>());
ind ans = 0;
int L = plus.size(), R = mul.size();
auto dfs = [&](auto &&dfs, int l, int r, ind cur, ind sum)
{
if (l == L && r == R)
{
ans = max(ans, sum);
re;
}
if (l < L)
{
dfs(dfs, l + 1, r, cur + plus[l], sum + cur + plus[l]);
}
if (r < R)
{
dfs(dfs, l, r + 1, cur * mul[r], sum + cur * mul[r]);
}
};
dfs(dfs, 0, 0, v, 0);
cout << fixed << setprecision(10) << ans / n << "\n";
}
某人此题开了 long double卡了半小时,这警示我们少开long double和long long。
K.库存
究极分讨题。
不妨依次讨论:
-
如果 m、nm、nm、n 都为 000 ,则 ans=0ans=0ans=0。
-
如果 nnn 为 000 ,x>yx>yx>y,自私都选 A ,那么 ans=xans=xans=x ;反之都选 B ,ans=m∗yans=m*yans=m∗y。
-
如果 mmm 为 000 ,x>yx>yx>y,无私一只选 A,其余选 B,那么 ans=x+(n−1)∗yans=x+(n-1)*yans=x+(n−1)∗y ;否则全选 B,ans=n∗yans=n*yans=n∗y。
-
如果 m、nm、nm、n 都不为 000:
-
x=0x=0x=0,ans=(m+n)∗yans=(m+n)*yans=(m+n)∗y;
-
y=0y=0y=0,ans=xans=xans=x;
x、yx、yx、y 都不为 000:
-
如果无私奶牛都选 B:x>yx>yx>y,ans1=x+n∗yans1=x+n*yans1=x+n∗y;反之,ans1=(n+m)∗yans1=(n+m)*yans1=(n+m)∗y;
-
如果无私有 ttt 只选 A,ans2=x+(n+m−t)∗yans2=x + (n + m - t) * yans2=x+(n+m−t)∗y。
ans=min(ans1,ans2)ans=min(ans1,ans2)ans=min(ans1,ans2)。
-
void solve()
{
cin >> n >> m >> x >> y;
if (n == 0 && m == 0)
{
cout << 0;
re;
}
if (n == 0)
{
if (x > y)
{
cout << x;
re;
}
else
{
cout << m * y;
re;
}
}
if (m == 0)
{
if (x > y)
{
cout << x + ((n - 1) * y);
re;
}
else
{
cout << n * y;
re;
}
}
int cnt1 = 0, cnt2 = 0;
if (m != 0 && n != 0)
{
if (x == 0)
{
cout << (m + n) * y;
re;
}
else
{
if (y == 0)
{
cout << x;
re;
}
else
{ // x,y都不为0
// 1、如果无私都选y
if (x > y)
{
cnt1 = x + n * y;
}
else
{
cnt1 = (n + m) * y;
}
// 2、如无私有t只选x,其余选y
t = (x - 1) / y;
if (t > 0 && t <= n)
{
cnt2 = x + (n + m - t) * y;
}
cout << max(cnt1, cnt2);
re;
}
}
}
}
A.染色
考虑贪心。
假设一开始所有颜色都通过涂满,即 ans[i]=w[i]+nans[i]=w[i]+nans[i]=w[i]+n,然后贪心地求能减少的值。
首先首尾的连续段不用涂色,记录断开连续的首尾位。
如果中间的连续段 cnt>w[c[i]]cnt>w[c[i]]cnt>w[c[i]],说明我们不需要涂满,可以从前涂到连续段的起始,从连续段的末尾涂到后。
即用 ans[c[i]]ans[c[i]]ans[c[i]]减去这段。
void solve(){
cin >> n;
vector<int> c(n + 2);
for (int i = 1; i <= n; i++)
{
cin >> c[i];
}
vector<int> w(n + 2);
vector<int> ans((int)2e5 + 50);
for (int i = 1; i <= n; i++)
{
cin >> w[i];
ans[i] = w[i] + n;
}
int i = 1;
int x = n, y = 1;
while (i <= n)
{
if (c[i] == c[1])
{
ans[c[i]]--;
i++;
}
else
{
x = i;
break;
}
}
i = n;
while (i >= x)
{
if (c[i] == c[n])
{
ans[c[i]]--;
i--;
}
else
{
y = i;
break;
}
}
if (x > y)
{
ans[c[1]] = 0;
}
int cnt = 1;
for (int j = x; j <= y; j++)
{
if (c[j] == c[j - 1])
{
cnt++;
}
else
{
cnt = 1;
}
if (cnt > w[c[j]])
{
ans[c[j]]--;
}
}
for (int i = 1; i <= n; i++)
{
cout << ans[i] << ' ';
}
}
E.打字
状压dp。
f[i][j]f[i][j]f[i][j]:i 状态下,此时光标位于 j 位置。
- 从 jjj 往前考虑,如果左移 kkk 的位置已经被打印,则让 cnt++cnt++cnt++ ;否则更新
- 从 jjj 向后考虑,同理。
int a[30][30];
int f[1ll << 21][30];
void solve()
{
cin >> n >> m >> t;
cin >> s1;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < m; j++)
{
cin >> a[i][j];
}
}
for (int i = 0; i < (1 << 21); ++i)
{
fill(f[i], f[i] + 30, inf);
}
for (int j = 0; j < n; j++)
f[1 << j][j] = a[0][(int)(s1[j] - 'a')];
for (int i = 0; i < (1ll << n); i++) //遍历状态
{
for (int j = 0; j < n; j++) //遍历位置
{
cnt = 1; //往前考虑,因为光标在最后,默认cnt为 1
for (int k = j - 1; k >= 0; k--)
{
int tp = i | (1ll << k);
if (i & (1ll << k)) //该位置已经被打印,继续左移,cnt+1
{
cnt++;
}
else
{
f[tp][k] = min(f[tp][k],
f[i][j] + t * cnt + a[(int)(s1[j] - 'a')][(int)(s1[k] - 'a')]);
}
}
cnt = 0;//往后考虑
for (int k = j + 1; k < n; k++)
{
int tp = i | (1ll << k);
if (i & (1ll << k))
{
cnt++;
}
else
{
f[tp][k] = min(f[tp][k],
f[i][j] + t * cnt + a[(int)(s1[j] - 'a')][(int)(s1[k] - 'a')]);
}
}
}
}
cout << n * t + *min_element(f[(1ll << n) - 1], f[(1ll << n) - 1] + n);
}