【杂题合集】震惊!这种生活习惯可能致癌!99%的人都有......

杂题合集

基本上都是不太难的思维题,难的我也写不出来。
标题党去死。

CF1714E Add Modulo 10

题意概述:

给出一个序列,可以给其中任意一个数加上当前它的个位数,问进行若干次操作后这个序列里的数能否全部相等。

解析:

思维题,不算难。

观察个位数,找规律:

  • 个位数是 0:只能是 0
  • 个位数是 112
  • 个位数是 224862
  • 个位数是 3362
  • 个位数是 44862
  • 个位数是 550
  • 个位数是 662
  • 个位数是 774862
  • 个位数是 8862
  • 个位数是 99862

可见,除个位数为 05 外,所有的数都可将个位数变为 2

对于个位数是 5 的数,就给它加上 5,此时只有其他数都与它相等,序列才合法。

对于其他数,都把它变成以 2 为个位数的数。注意到个位数从 2 再变到 2,相当于加了 20
将变化后的数列sort一遍,判断相邻的两数之差是不是 20 的倍数就行了。

Code

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 2e5 + 10;
int t, n;
int num[MAXN];
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
bool Check(){
sort(num + 1, num + 1 + n);
if(num[1] % 10 == 0){
if(num[n] == num[1]) return true;
else return false;
}
for(register int i = 2; i <= n; i++){
int cut = num[i] - num[i - 1];
if(cut % 20) return false;
}
return true;
}
int main(){
t = read();
while(t--){
n = read();
for(register int i = 1; i <= n; i++){
num[i] = read();
if(num[i] % 10 != 2){
switch(num[i] % 10){
case 1 : num[i] += 1; break;
case 3 : num[i] += 9; break;
case 4 : num[i] += 18; break;
case 5 : num[i] += 5; break;
case 6 : num[i] += 6; break;
case 7 : num[i] += 25; break;
case 8 : num[i] += 14; break;
case 9 : num[i] += 23; break;
}
}
}
if(Check()) puts("Yes");
else puts("No");
}
return 0;
}

CF1711B Party

题目概述:

给出一个 n 个点,m 条边的无向图,问删去多少的点使得删去的点权最小,且留下的点组成的图中有偶数条边。输出删去的点的点权和。

解析:

如果 m 为偶数,不用删点。

如果 m 为奇数,删点的同时删掉的边要为奇数条,大力分类讨论:

  1. 删一个点:显然要选择一个度数为奇数的点。
  2. 删两个点:再度大力分类讨论:
    1. 删去两个相连的,且度数都为偶数的点。
    2. 删去两个相连的,且度数都为奇数的点。
    3. 删去不相连的,度数一奇一偶的点:
      但是我们可以只删去那个度数为奇数的点,这样做会更优,所以这种情况一定不会有最优解。
  3. 删三个点:先考虑删去一个度数为奇,两个度数为偶且两两不相连的点。但这样完全可以只删去那个度数为奇的点,所以删去三个点一定不会有最优解。

所以,最优解只有可能在三种情况中出现,即删掉一个度数为奇的点、删掉有相连的两个度数都为奇的点、删掉相连的两个度数都为偶数的点。

因为 n,m105,删去一个点的直接枚举点,删去两个点的直接枚举边即可。

Code

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e5 + 10, MAXM = 1e5 + 10;
int t, n, m, ans;
int unhap[MAXN];
int from[MAXM], to[MAXM], deg[MAXN];
inline void Clear(){
ans = 2147483647;
memset(deg, 0, sizeof(deg));
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int main(){
t = read();
while(t--){
Clear();
n = read(), m = read();
for(register int i = 1; i <= n; i++)
unhap[i] = read();
for(register int i = 1; i <= m; i++){
int u, v;
u = read(), v = read();
deg[u]++, deg[v]++;
from[i] = u, to[i] = v;
}
if(!(m & 1)){
puts("0");
continue;
}
for(register int i = 1; i <= n; i++)
if(deg[i] & 1) ans = min(ans, unhap[i]);
for(register int i = 1; i <= m; i++){
int u = from[i], v = to[i];
if(!((deg[u] + deg[v]) & 1))
ans = min(ans, unhap[u] + unhap[v]);
}
printf("%d\n", ans);
}
return 0;
}

CF1605C Dominant Character

题目概述:

给定一个由 abc 组成的字符串,找出一个最短的子串,满足 a 的数量大于 b 的数量和 c 的数量。

解析:

大力讨论:

  1. 串长为 2,两个 a 相邻。
  2. 串长为 3,所以仅当两个 a 中间隔了一个 bc,即 abcaca
  3. 串长为 4a 的数量至少是 2a 不能相邻,只能是 abcaacba,不会有 3a的情况。
  4. 串长为 5a 的数量至少是 3,则会有长为 3 的子串中有两个 a,不成立。
  5. 串长为 6a 的数量至少是 3,会出现长为 3 的子串中有两个 a,不成立。
  6. 串长为 7a 的数量至少是 4,且a 之间需要有三个不是 a 的字母隔开。

之后以此类推,发现答案只可能到 7,再长的子串都能都被拆分成更短的满足要求的子串,之后枚举即可。

点击查看代码

Code

#include<cstdio>
using namespace std;
const int MAXN = 1e6 + 10;
int t, n;
char s[MAXN];
int main(){
scanf("%d", &t);
while(t--){
bool flag = false;
scanf("%d", &n);
scanf("%s", s + 1);
for(register int i = 2; i <= n && !flag; i++){
if(s[i] == 'a' && s[i - 1] == 'a'){
puts("2");
flag = true;
}
}
if(n >= 3)
for(register int i = 2; i <= n - 1 && !flag; i++){
if(s[i - 1] == 'a' && s[i + 1] == 'a'){
puts("3");
flag = true;
}
}
if(n >= 4)
for(register int i = 2; i <= n - 2 && !flag; i++){
if(s[i - 1] == 'a' && s[i + 2] == 'a' && ((s[i] == 'b' && s[i + 1] == 'c') || (s[i] == 'c' && s[i + 1] == 'b'))){
puts("4");
flag = true;
}
}
if(n >= 7)
for(register int i = 4; i <= n - 3 && !flag; i++){
if(s[i] == 'a' && s[i - 3] == 'a' && s[i + 3] == 'a'){
if(s[i - 1] == 'b' && s[i - 2] == 'b' && s[i + 1] == 'c' && s[i + 2] == 'c'){
puts("7");
flag = true;
}
else if(s[i - 1] == 'c' && s[i - 2] == 'c' && s[i + 1] == 'b' && s[i + 2] == 'b'){
puts("7");
flag = true;
}
}
}
if(!flag) puts("-1");
}
return 0;
}

CF1714G Path Prefixe

题目概述:

给你一颗以一号节点为根的树,每个节点上有两个权值,分别为 aibi。记 Ai 为从根到节点 iai 的前缀和,求最长的使得 bi 的和小于等于 Ai 的前缀。

解析:

其实题目概述写啰嗦了,我的确概述不了这道题。读懂了题就很简单了。

看数据范围 n2×105,以及题目中的最长不大于,可以往二分上靠。

树论的话 dfs 是必不可少的,用 dfs 求出每个节点 ab 的前缀和,把当前节点的 b 的前缀和压进栈里,本层的 dfs 结束时再把它弹出栈,就可以保证栈里的元素全部是从根节点到当前节点路径上,每个点的 b 的前缀和,upper_bound 一下即可求出最长的前缀。

注意开 long longCF Div.3 光卡 long long

Code

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int MAXN = 2e5 + 10;
int t, n, cnt, top;
int head[MAXN], ans[MAXN];
LL sum_a[MAXN], sum_b[MAXN];
LL stk[MAXN];
struct Edge{
int to, next, dis_a, dis_b;
}e[MAXN << 1];
inline void Add(int u, int v, int a, int b){
e[++cnt].to = v;
e[cnt].dis_a = a;
e[cnt].dis_b = b;
e[cnt].next = head[u];
head[u] = cnt;
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void dfs(int rt, int fa){
stk[++top] = sum_b[rt];
for(register int i = head[rt]; i; i = e[i].next){
int v = e[i].to;
if(v == fa) continue;
sum_a[v] = sum_a[rt] + e[i].dis_a;
sum_b[v] = sum_b[rt] + e[i].dis_b;
dfs(v, rt);
}
int pos = upper_bound(stk + 1, stk + 1 + top, sum_a[rt]) - stk;
ans[rt] = pos - 2;
top--;
}
void Clear(){
cnt = 0;
top = 0;
memset(head, 0, sizeof(head));
}
int main(){
t = read();
while(t--){
Clear();
n = read();
for(register int i = 2; i <= n; i++){
int p, a, b;
p = read(), a = read(), b = read();
Add(p, i, a, b);
Add(i, p, a, b);
}
dfs(1, 0);
for(register int i = 2; i <= n; i++)
printf("%d ", ans[i]);
puts("");
}
return 0;
}

CF1714F Build a Tree and That Is It

题目概述:

概不出来了自己品吧。

树是一个没有环的无向连通图,注意,在本题中,我们讨论的是无根树

现有四个整数 n,d12,d23d31,构建一颗满足以下条件的树:

  • 包含从 1nn 个节点;
  • 从节点 1 到节点 2 的距离(最短路的长度)为 d12
  • 从节点 2 到节点 3 的距离为 d23
  • 从节点 3 到节点 1 的距离为 d31

输出满足条件的任意一棵树;若不存在,输出 NO

解析:

每条边的边权为 1

题目说是无根树,可以先假设它有一个根节点,设 d1d2d3 三条互不相交的分别连接根节点和 123 号节点的链的长度。

然后就可得到:

{d12=d1+d2d23=d2+d3d31=d1+d3

只后我们移项,消元:

{d1=d12+d31d232d2=d12+d23d312d3=d23+d31d122

其中 d1d2d3 可能为 0,代表该节点就是根节点。

至于无解的情况,显然,d1d2d3 都要 0,且d12+d31d23d12+d23d31d23+d31d12 都应为偶数。

构造完这 3 条链后的树即为满足 3 个点之间的约束条件的最小的树,判断总的节点数 tot=d1+d2+d3+1 (从根节点到 123 号节点的链的长度就是链上的节点数,最后要加上根节点)与 n 的大小关系:

  • tot<n,则剩余的节点在任意节点上都不会影响树的合法性。
  • tot=n,构造结束。
  • tot>n,不合法,构造失败。

之后我们需要找出根节点是谁,若 d1d2d3 都不为 0,我们可以让任意一个 3n 的节点为根节点,这里选择了 4 号节点。

最后剩下的节点随便连谁都可以,这里选择接在 3 号节点。

Code

点击查看代码
#include<cstdio>
using namespace std;
int t, n, d12, d23, d31;
int root, tot, d[4];
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void Build_Tree(int x){
printf("%d ", root);
for(register int i = 1; i < d[x]; i++){
++tot;
printf("%d\n", tot);
printf("%d ", tot);
}
printf("%d\n", x);
}
void Clear(){
tot = 0;
root = 0;
d[1] = d[2] = d[3] = 0;
}
int main(){
t = read();
while(t--){
Clear();
n = read();
d12 = read(), d23 = read(), d31 = read();
d[1] = (d12 + d31 - d23) / 2;
d[2] = (d12 + d23 - d31) / 2;
d[3] = (d23 + d31 - d12) / 2;
if(d[1] + d[2] + d[3] + 1 > n || d[1] < 0 || d[2] < 0 || d[3] < 0){
puts("NO");
continue;
}
if((d12 + d31 - d23) & 1 || (d12 + d23 - d31) & 1 || (d23 + d31 - d12) & 1){
puts("NO");
continue;
}
puts("YES");
tot = 3;
if(!d[1]) root = 1;
if(!d[2]) root = 2;
if(!d[3]) root = 3;
if(!root){
tot = 4;
root = 4;
}
if(1 != root) Build_Tree(1);
if(2 != root) Build_Tree(2);
if(3 != root) Build_Tree(3);
while(tot < n) printf("3 %d\n", ++tot);
}
return 0;
}

P7913 [CSP-S 2021] 廊桥分配

没考试,刷点历年原题。看着它是21年的T1,应该好欺负,然后想了一中午才想出来。

题目概述:

概NM,自己看。

解析:

廊桥先到先得,仅于飞机的起降顺序有关,加入第 i 条廊桥不会对前 i1 条廊桥的分配产生影响。我们设 f(x) 代表分配 x 条廊桥能停靠的国内航班数,g(x) 代表分配 x 条廊桥能停靠的国际航班数,可得 f(x)=f(x1)+g(x)=g(x1)+

考虑每加入一条廊桥会影响哪些飞机。用 set 来维护当前在远机位和未降落的飞机。每加入一条廊桥,当前 set 顶端的飞机 p 停靠到廊桥,之后,set 中在 p 起飞后第一个降落的飞机 p 停靠到廊桥......以此类推。

直接在 set 中二分查找,记录次数,然后再把找到的飞机清出 set(停靠过一遍廊桥即起飞),最后加入这条廊桥的贡献就是找到的飞机的次数。

Code

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1e5 + 10;
int n, m1, m2, ans;
int sum1[MAXN], sum2[MAXN];
struct Plane{
int st, ed;
bool operator < (const Plane &a) const{
return st < a.st;
}
}p1[MAXN], p2[MAXN];
multiset<Plane> s1, s2;
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int Get_Num(multiset<Plane> &s){
int ans = 0;
auto pos = s.begin();
if(pos == s.end()) return 0;
Plane p = *pos;
p.st = p.ed;
++ans;
s.erase(pos);
while(!s.empty()){
pos = s.lower_bound(p);
if(pos == s.end()) break;
++ans;
p = *pos;
p.st = p.ed;
s.erase(pos);
}
return ans;
}
int main(){
n = read(), m1 = read(), m2 = read();
for(register int i = 1; i <= m1; i++){
p1[i].st = read(), p1[i].ed = read(); //国内航班
s1.insert(p1[i]);
}
for(register int i = 1; i <= m2; i++){
p2[i].st = read(), p2[i].ed = read(); //国外航班
s2.insert(p2[i]);
}
sort(p1 + 1, p1 + 1 + m1);
sort(p2 + 1, p2 + 1 + m2);
for(register int i = 1; i <= n; i++)
sum1[i] = sum1[i - 1] + Get_Num(s1);
for(register int i = 1; i <= n; i++)
sum2[i] = sum2[i - 1] + Get_Num(s2);
for(register int i = 0; i <= n; i++)
ans = max(ans, sum1[i] + sum2[n - i]);
printf("%d", ans);
return 0;
}

P2515 [HAOI2010]软件安装

边写边颓,结果一A了(坏了,我成wonder了)。

题目概述:

自己概去,语文功底不好,概不出来。

解析:

最开始当图论题打的,结果越打越不对,发现是个树上的背包问题。

首先,每个软件之间有依赖关系,即软件 i 只有在安装了软件 Di 之后才能正常工作,考虑从 Dii 连边,最后会得到一个类似树形的关系。然后就理所应当的想到拓扑排序或者树形DP了是吧。

问题在于这个关系并不能直接构成树,每个关系(每条边)都是单向的,所以可能出现游离在外的点或环,比如 D1=2,D2=3,D3=1,这时 1,2,3 就形成了一个独立的环。这个环要么都选,要么都不选,所以可以用tarjan缩点,把环缩成一个联通块。

缩完点之后仍然有独立的点,需要把它变成一个树状结构。考虑将所有入度为 0 的点和 0 连一条边,这样这张图就可以转化成一棵以 0 为根的树,然后就可以在树上跑01背包了。

设定状态 dp[i][j] 代表以 i 为根的子树在 j 的容量下能得到的最大的价值,在递归到当前节点的时候记得初始化一下DP数组就行了。为啥不讲转移?就是个普通的01背包,讲啥啊。

Code

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 110, MAXM = 510;
int n, m, cnt, num, tot;
int head[MAXN], spa[MAXN], val[MAXN], from[MAXN], to[MAXN];
int dfn[MAXN], low[MAXN], belong[MAXN], room[MAXN], value[MAXN], in[MAXN];
int stk[MAXN], top;
int dp[MAXN][MAXM];
bool vis[MAXN];
struct Edge{
int to, next;
}e[MAXN << 1];
inline void Add(int u, int v){
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void Tarjan(int u){
dfn[u] = low[u] = ++num;
stk[++top] = u;
vis[u] = true;
for(register int i = head[u]; i; i = e[i].next){
int v = e[i].to;
if(!dfn[v]){
Tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]){
++tot;
int t;
do{
t = stk[top--];
vis[t] = false;
belong[t] = tot;
room[tot] += spa[t];
value[tot] += val[t];
}while(t != u);
}
}
void Rebuild(){
cnt = num = 0;
memset(head, 0, sizeof(head));
for(register int i = 1; i <= n; i++){
int u = from[i], v = to[i];
int blu = belong[u], blv = belong[v];
if(!u) continue;
if(blu != blv){
Add(blu, blv);
++in[blv];
}
}
for(register int i = 1; i <= tot; i++)
if(!in[i]) Add(0, i);
}
void dfs(int rt){
for(register int i = room[rt]; i <= m; i++)
dp[rt][i] = value[rt];
for(register int i = head[rt]; i; i = e[i].next){
int v = e[i].to;
dfs(v);
int k = m - room[rt];
for(register int i = k; i >= 0; i--)
for(register int j = 0; j <= i; j++)
dp[rt][i + room[rt]] = max(dp[rt][i + room[rt]], dp[v][j] + dp[rt][i + room[rt] - j]);
}
}
int main(){
n = read(), m = read();
for(register int i = 1; i <= n; i++)
spa[i] = read();
for(register int i = 1; i <= n; i++)
val[i] = read();
for(register int i = 1; i <= n; i++){
int u;
u = read();
if(u) Add(u, i);
from[i] = u, to[i] = i;
}
for(register int i = 1; i <= n; i++)
if(!dfn[i]) Tarjan(i);
Rebuild();
dfs(0);
printf("%d\n", dp[0][m]);
return 0;
}
posted @   TSTYFST  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示