【2025.8.13】模拟赛
T1
solution
考虑每个质数能产生的贡献是它的所有倍数项的贡献,即对于质数 \(p\),只有 \(p^{n + 1 - p},(2p)^{n + 1 - 2p},\cdots,(p\lfloor \frac{n}{p} \rfloor)^{n + 1 - p\lfloor \frac{n}{p} \rfloor}\) 这些项有贡献。
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e7+5;
int n;
int p[665000],cnt=0;
bool f[N];
ll sum[665000];
void pre(){
memset(f,1,sizeof(f));
for(int i=2;i<=n;++i){
if(f[i]) p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=n;++j){
f[i*p[j]]=0;
if(i%p[j]==0) break;
}
}
}
ll calc(int x){
int num=n/x;
return 1ll*num*(n+1)-1ll*x*num*(num+1)/2;
}
int main(){
freopen("factorial.in","r",stdin);
freopen("factorial.out","w",stdout);
cin>>n;
pre();
for(int i=1;i<=cnt;++i){
ll k=0;
ll c=p[i];
while(1){
k+=calc(c);
c*=p[i];
if(c>n) break;
}
sum[i]=k;
}
printf("f(%d)=",n);
if(sum[1]==1) printf("2");
else printf("2^%lld",sum[1]);
for(int i=2;i<=cnt;++i){
if(sum[i]==1) printf("*%d",p[i]);
else printf("*%d^%lld",p[i],sum[i]);
}
printf("\n");
fclose(stdin);fclose(stdout);
return 0;
}
T2
solution
无向图中两点间的简单路径必过一点,此点为割点。
那么能两两连边的就只有同一个点双中的点。
注意:割点连接的是点双。
(不知道为什么,以前一直记的是割点连边双,割边连点双)
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5;
int read(){
int num=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
num=(num<<1)+(num<<3)+ch-'0';
ch=getchar();
}
return num*f;
}
int n,m;
ll sum=0;
vector<int> G[N];
int dfn[N],low[N],cnt=0,bcc=0;
stack<int> s;
vector<int> ans[N];
void tarjan(int u,int fa){
dfn[u]=++cnt;
low[u]=cnt;
s.push(u);
for(int i=0;i<G[u].size();++i){
int v1=G[u][i];
if(v1==fa) continue;
if(!dfn[v1]){
tarjan(v1,u);
low[u]=min(low[u],low[v1]);
if(low[v1]>=dfn[u]){
++bcc;
while(s.top()!=v1){
ans[bcc].push_back(s.top());
s.pop();
}
ans[bcc].push_back(s.top());
s.pop();
ans[bcc].push_back(u);
}
}
else low[u]=min(low[u],dfn[v1]);
}
if(fa==0&&G[u].size()==0) ans[++bcc].push_back(u);
}
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
n=read();m=read();
for(int i=1;i<=m;++i){
int ui=read(),vi=read();
G[ui].push_back(vi);
G[vi].push_back(ui);
}
for(int i=1;i<=n;++i)
if(!dfn[i]){
while(!s.empty()) s.pop();
tarjan(i,0);
}
for(int i=1;i<=bcc;++i){
ll t=1ll*ans[i].size();
sum+=t*(t-1)/2;
}
printf("%lld\n",sum);
fclose(stdin);fclose(stdout);
return 0;
}
T3
solution
统计满足一定条件的数的数量(即计数),对数位有要求,提供数字区间限制,上界很大,暴力会超时,考虑用数位 DP。
从高位到低位确定满足条件的数,枚举一个前缀与 \(x\) 一致,接下来的一位比 \(x\) 小,考虑一下此时后面位的取值就是还没有被确定的那些位的任意选择。我们用并查集把限制关系维护一下,很容易计算出满足条件的数。
code
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
const int mod=19260817;
int n,m;
int a[N],b[N];
int fa[N];
int cf[N];
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
int solve(int c[]){
int tot=0;
int res=0;
for(int i=1;i<=n;++i)
if(fa[i]==i)
tot++;
for(int i=n;i>=1;--i){
if(fa[i]==i){
tot--;
res=(res+c[i]*cf[tot])%mod;
}
if(c[find(i)]<c[i]) res=(res+cf[tot])%mod;
if(c[find(i)]!=c[i]) break;
}
return res;
}
int main(){
freopen("counting.in","r",stdin);
freopen("counting.out","w",stdout);
cin>>n;
cf[0]=1;
for(int i=1;i<=n;++i){
fa[i]=i;
cf[i]=cf[i-1]*10%mod;
}
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i) scanf("%d",&b[i]);
cin>>m;
for(int i=1;i<=m;++i){
int ui,vi;
scanf("%d%d",&ui,&vi);
int uf=find(ui),vf=find(vi);
if(uf==vf) continue;
fa[min(uf,vf)]=max(uf,vf);
}
printf("%d\n",(solve(b)-solve(a)+mod)%mod);
fclose(stdin);fclose(stdout);
return 0;
}
T4(原题解)
solution
考虑时刻我们购买的灯笼都一定形成一个区间(否则一定有一段是目前没用的),我们设 dpx,y 表示当前区间左端点为 \(a_x\), 右端点为 \(b_y\), 我们可以处理出来每个灯笼的 \(a_x\) 和 \(b_x\) 对能到达的区间的限制, \(dp_{x,y}\) 所代表的区间就是 \(a_x\) 的限制和 \(b_y\) 的限制的交集。
考虑转移顺序, 我们按照 \(a_x\) 递增, \(b_x\) 递减的顺序遍历每一个状态, 考虑转移有三种情况。
- \(dp_{x,y′} + c_{y′}\to dp_{x,y},y′\) 在 \((x,y)\) 限制区间内。
- \(dp_{x′,y} + c_{x′}\to dp_{x,y},x′\) 在 \((x,y)\) 限制区间内。
- \(dp_{x′,x′} + c_{x′}\to dp_{x,y},x′\) 在 \((x,y)\) 限制区间内。 考虑前两种转移, 对于同一个 \(x\),\(b_{y1} < b_{y2}\), 那么 \((x,y_1)\) 的限制区间一定是 \((x,y_2)\) 的限制区间的子区间, 于是我们可以用一个优先队列维护 \(dp_{x,y′} + c_{y′}\), 堆顶转移不了 pop 即可。
考虑第三种转移, 因为 \(dp_{x′,x′}\) 种类很少其实也不是什么问题, 我们把 \(dp_{x′,y}\), 其中 \((a_y,b_y)\) 是 \((a_{x′},b_{x′})\) 的子区间的 \(dp\) 值设为 \(dp_{x′,x′}\), 用上面的转移即可。
code
#include <bits/stdc++.h>
#define N 2100
using namespace std;
int n, k, h[N], p[N], c[N], a[N], b[N], lim[N][2][2], dp[N][N];
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>
ql[N], qr[N];
int main() {
//freopen("lantern.in", "r", stdin);
//freopen("lantern.out", "w", stdout);
cin.tie(0)->sync_with_stdio(0);
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> h[i];
for (int i = 1; i <= k; i++) {
cin >> p[i] >> c[i] >> a[i] >> b[i];
lim[i][0][0] = p[i] + 1;
while (lim[i][0][0] > 1 && h[lim[i][0][0] - 1] >= a[i])
lim[i][0][0]--;
lim[i][0][1] = p[i] - 1;
while (lim[i][0][1] < n && h[lim[i][0][1] + 1] >= a[i])
lim[i][0][1]++;
lim[i][1][0] = p[i] + 1;
while (lim[i][1][0] > 1 && h[lim[i][1][0] - 1] <= b[i])
lim[i][1][0]--;
lim[i][1][1] = p[i] - 1;
while (lim[i][1][1] < n && h[lim[i][1][1] + 1] <= b[i])
lim[i][1][1]++;
}
vector<int> vl, vr;
for (int i = 1; i <= k; i++)
vl.push_back(i), vr.push_back(i);
sort(vl.begin(), vl.end(),
[&](int x, int y) { return a[x] == a[y] ? b[x] > b[y] : a[x] < a[y]; });
sort(vr.begin(), vr.end(),
[&](int x, int y) { return b[x] == b[y] ? a[x] < a[y] : b[x] > b[y]; });
memset(dp, -1, sizeof(dp));
for (auto i : vl)
for (auto j : vr) {
if (i != j && a[i] <= a[j] && b[i] >= b[j]) {
dp[i][j] = dp[i][i];
} else if (i != j && a[j] <= a[i] && b[j] >= b[i]) {
dp[i][j] = dp[j][j];
} else if (a[i] <= a[j] && b[i] <= b[j]) {
int l = max(lim[i][0][0], lim[j][1][0]),
r = min(lim[i][0][1], lim[j][1][1]);
if (l > min(p[i], p[j]) || r < max(p[i], p[j]))
continue;
if (l == 1 && r == n) {
dp[i][j] = 0;
} else {
while (!ql[i].empty() &&
(p[ql[i].top().second] < l || p[ql[i].top().second] > r ||
a[ql[i].top().second] > b[j]))
ql[i].pop();
if (!ql[i].empty()) {
int tmp = ql[i].top().first;
if (dp[i][j] == -1 || dp[i][j] > tmp)
dp[i][j] = tmp;
}
while (!qr[j].empty() &&
(p[qr[j].top().second] < l || p[qr[j].top().second] > r ||
b[qr[j].top().second] < a[i]))
qr[j].pop();
if (!qr[j].empty()) {
int tmp = qr[j].top().first;
if (dp[i][j] == -1 || dp[i][j] > tmp)
dp[i][j] = tmp;
}
}
}
if (dp[i][j] != -1) {
ql[i].push({dp[i][j] + c[j], j});
qr[j].push({dp[i][j] + c[i], i});
}
}
for (int i = 1; i <= k; i++) {
cout << (dp[i][i] == -1 ? -1 : dp[i][i] + c[i]) << '\n';
}
return 0;
}