补题链接 : https://qoj.ac/contest/2564
J. 后鼻嘤
在每个以 n 结尾的字符串后添加 g 并输出
读入一整行法
//2025-11-02
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>using namespace std;
const int N = 1e5+10;
#define LL long longvoid solve() {char s[N];cin.getline(s,100000);for(int i=0;;i++) {if((s[i]>'z'||s[i]<'a')&&s[i]!=' ') break;cout<<s[i];if(s[i]=='n'&&(s[i+1]>'z'||s[i+1]<'a'))cout<<'g';}}int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);solve();return 0;
}
持续读⼊字符串法
//2025-11-02
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>using namespace std;
#define LL long longvoid solve() {char s[10];bool flag=0;while(cin>>s) {if(flag) {cout<<" ";}flag=1;int n=strlen(s);cout<<s;if(s[n-1]=='n') cout<<'g';}
}int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);solve();return 0;
}
A.环状线
简单的分类讨论。
暴力模拟也可以通过。
//2025-11-02
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>using namespace std;
int n,s,t;
bool solve() {int dis1,dis2;if(s<t) {dis1=t-s;dis2=n-(t-s);}else{dis1=n-(s-t);dis2=s-t;}return dis1<dis2;
}int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin>>n>>s>>t;puts(solve()?"1":"2");return 0;
}
G. 最大公约数
从\([1,n]\)中选出\(k\)个不同的整数,使得两两互质。
贪心,筛出小于\(n\)的素数(设有\(cntp\)个),那么\([1,n]\)中最多有\(cntp+1\)个数两两互质(1和任意数互质)
线性筛的模板。
//2025-11-02
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>using namespace std;
const int N = 5e5+10;
#define LL long long
int n,k;
int prime[N], mindiv[N];
int cntp;
void Euler(int n) {for (int i = 2; i <= n; i++) {if (!mindiv[i])mindiv[i] = prime[++cntp] = i;for (int j = 1; j <= cntp && i * prime[j] <= n && prime[j] <= mindiv[i]; j++)mindiv[i * prime[j]] = prime[j];}
}
void solve() {cin>>n>>k;Euler(n);prime[0]=1;if(k<=cntp+1) {cout<<"YES"<<endl;for(int i=0;i<k;i++) {cout<<prime[i]<<" ";}}else{cout<<"NO"<<endl;}
}int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);solve();return 0;
}
C. 短视频
模拟,\(O(n)\)遍历每个视频即可
设当前累积的观看时长为sum
注意:每个视频都会至少看1s,因为是在这1s结束后判断是否继续刷。
//2025-11-02
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>using namespace std;
const int N = 2e5+10;
#define LL long longLL n,T;
LL t[N],k[N];
LL sum;
int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin>>n>>T;for(int i=1;i<=n;i++) {cin>>t[i]>>k[i];}for(int i=1;i<=n;i++) {if(sum<=T-t[i]) sum+=t[i];//累计不超Telse{if(sum-T<=k[i]-t[i]) sum+=t[i];//吸引力足够支撑到视频结束elsesum=max(sum+1,T+k[i]+1);//吸引力中间不足}}cout<<sum;return 0;
}
K. 左儿子右兄弟
这道题想起来比较简单,代码实现有些细节不好写。
题目有两问:
-
所有节点子树大小之和最小是多少
-
满足1有几种情况
对于1,显然是每个点子树和最大的点排在前面(因为排在越后面,深度越大,统计子树和时累加次数越多)
如果一个点的几个儿子的子树大小相等,相等的这几个顺序可以是任意的,这就形成了多种情况,也就是第二问让求的。假设有\(k\)个子树大小都为\(w\)的儿子,那么方案数就乘\(k!\),dfs时统计方案数即可。
首先原树建图后dfs求各点的子树和siz。
第一问:dfs回溯时给当前点u的所有儿子的siz排序(大到小),最大的\(k_1\)做u的儿子,然后\(k_2\)做\(k_1\)的儿子,依次……建一个新树。然后dfs新树求各点的新子树和ans,再加和即为答案res1。
第二问:记结果为res2,刚才排序后,直接统计每个子树大小的有几个,然后乘\(k!\)。
//2025-11-03
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>using namespace std;
const int N = 2e5+10;
#define LL long long
const LL mod=998244353;int n;
int to[N],nxt[N],hd[N],ecnt;
void add(int x,int y) {to[++ecnt]=y;nxt[ecnt]=hd[x];hd[x]=ecnt;
}
vector<int>g[N];//新图
LL siz[N];LL fac[N];//阶乘
LL res1,res2=1;
struct node{int u;LL size;node(int uu,LL s):u(uu),size(s){}bool operator < (const node& x)const{return size>x.size;}
};
vector<node>ve[N];
void dfs(int x,int fa) {for(int i=hd[x];i;i=nxt[i]) {int& y=to[i];dfs(y,x);ve[x].push_back(node(y,siz[y]));siz[x]+=siz[y];}if(ve[x].empty()) return;sort(ve[x].begin(),ve[x].end());int pre=x,cnt=1;for(auto k:ve[x]) {if(k.size==siz[pre]&&pre!=x) {cnt++;}else{res2=res2*fac[cnt]%mod;cnt=1;}g[pre].push_back(k.u);pre=k.u;}res2=res2*fac[cnt]%mod;
}
LL siz2[N];
void getRes1(int x,int fa) {for(auto y:g[x]) {getRes1(y,x);siz2[x]+=siz2[y];}
}
int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin>>n;int x;fac[0]=1;for(int i=1;i<=n;i++) {siz[i]=1;siz2[i]=1;fac[i]=fac[i-1]*i%mod;}for(int i=2;i<=n;i++) {cin>>x;add(x,i);}dfs(1,0);getRes1(1,0);for(int i=1;i<=n;i++) res1=res1+siz2[i];//注意res1不用取模cout<<res1<<"\n"<<res2;return 0;
}
B. 爬⼭
分层图最短路。
首先很容易想到是最短路,然后看到疲劳值清空,H很小,就能联想到分层图了。
按照疲劳值建H+1层图即可。
对于每条边:
上坡,在不超过H的情况下,从当前层连到上坡后对应疲劳值层;
下坡,从当前层到第0层连边。
//2025-11-09
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>using namespace std;
const int N = 2e6;
const int M = 2e7;
#define LL long long
const LL inf = 1e18;
int n, m, H;
int h[N];
int to[M], nxt[M], hd[N], ecnt;
LL w[M];
void add(int x, int y, LL z) {to[++ecnt] = y;w[ecnt] = z;nxt[ecnt] = hd[x];hd[x] = ecnt;
}
struct node {int u;LL val;node() {}node(int uu, LL vv): u(uu), val(vv) {}bool operator < (const node&x) const {return val > x.val;}
};
priority_queue<node> q;
LL dis[N];
bool vis[N];
void dij(int s) {for (int i = 1; i <= (H + 1) * n; i++) dis[i] = inf;q.push(node(s, 0));dis[s] = 0;while (!q.empty()) {int x = q.top().u;q.pop();if (vis[x]) continue;vis[x] = 1;for (int i = hd[x]; i; i = nxt[i]) {int& y = to[i];if (dis[y] > dis[x] + w[i]) {dis[y] = dis[x] + w[i];q.push(node(y, dis[y]));}}}
}
int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> n >> m >> H;for (int i = 1; i <= n; i++) cin >> h[i];for (int i = 1, u, v, t; i <= m; i++) {cin >> u >> v >> t;if (h[u] > h[v]) {for (int j = 0; j <= H; j++) {add(u + j * n, v, t); // 下坡if (j + (h[u] - h[v]) <= H)add(v + j * n, u + (j + (h[u] - h[v])) * n, t); // 上坡}} else if (h[u] < h[v]) {for (int j = 0; j <= H; j++) {add(v + j * n, u, t); // 下坡if (j + (h[v] - h[u]) <= H) {add(u + j * n, v + (j + (h[v] - h[u])) * n, t); // 上坡}}} else {// 平路for (int j = 0; j <= H; j++) {add(u + j * n, v + j * n, t);add(v + j * n, u + j * n, t);}}}dij(1);for (int i = 2; i <= n; i++) {LL ans = inf;for (int j = 0; j <= H; j++)ans = min(ans, dis[j * n + i]);if (ans != inf)cout << ans << " ";elsecout << "-1 ";}return 0;
}
其他题目待补。。。