The 10th Shandong Provincial Collegiate Programming Contest

https://codeforces.com/gym/104459

*G. Heap

题意

有一个初始序列,现将每个元素插入堆中。每个元素插入时会按照大根堆或小根堆的规则插入。给出初始序列和最终序列,问每次插入是将它视作大根堆还是小根堆,若有多种可能,输出字典序最小的。

题解

由于每次插入只会影响从当前位置到根的路径上的点,所以直接模拟就可以了。倒序枚举每个元素,找到当前元素最终落在了路径上的哪里,检验路径上经过的元素已经最终位置的父亲是否符合一个统一的大根堆或小根堆规则。如果该元素落在了路径最底下并且它的父亲和它相等,那么都有可能,为了字典序最小,我们认为它是小根堆。

代码

H. Tokens on the Segments

题意

有一些线段 [li,ri][l_i,r_i],你可以在 xx 轴上放置一些不重合的点,每个点只能属于一条线段,问至少有多少条线段满足至少含有一个点。

题解

贪心。

首先,对于 ll 相同的线段,我们肯定优先处理 rr 较小的,给它们分配点。但是,当 ll 上的点指派给某条线段以后,剩下的以 ll 为左端点的线段左端点就相当于变成了 l+1l+1。也就是说,记已被分配的最大的点右侧的点为 xx,那么所有左端点小于等于 xx 的线段都可以视作左端点为 xx。此时,要处理的下一条线段是它们中 rr 最小的。

所以我们得到了这样的流程:将线段按左端点升序排序。用优先队列维护待处理线段的右端点(按升序排序),同时记录下上面提到的 xx。每次将优先队列中右端点小于 xx 的弹出,若优先队列为空,则将 xx 移动到下一条线段的左端点,并且将所有左端点等于 xx 的线段加入进来;若优先队列不为空,则将 xx 分配给这条线段。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=1e5+5;
pii a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;cin>>t;
while(t--)
{
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].first>>a[i].second;
sort(a+1,a+n+1);
int id=1,x=-1,ans=0;
priority_queue<int,vector<int>,greater<int>> q;
while(1)
{
while(!q.empty()&&q.top()<x) q.pop();
if(q.empty())
{
if(id>n) break;
x=a[id].first;
}
else
{
q.pop();
ans++;
x++;
}
while(id<=n&&a[id].first==x)
{
q.push(a[id].second);
id++;
}
}
cout<<ans<<'\n';
}
return 0;
}

*I. Connected Intervals

题意

给出一棵树,求所有的区间 [l,r](1lrn)[l,r](1\leq l\leq r\leq n),满足所有标号在 [l,r][l,r] 内的点构成的导出子图是一个连通块。

题解

f(l,r)f(l,r) 为区间 [l,r][l,r] 内的点构成的导出子图的连通块数量。考虑将 rr 加入的过程,若 rr 有两个相邻的点 u,v[l,r1]u,v\in [l,r-1]。那么在 [l,r1][l,r-1]u,vu,v 一定在两个不同的连通块内。所以,对于 rr,所有满足 u[l,r1]u\in [l,r-1]uu 的数量就是由 f(l,r1)f(l,r-1)f(l,r)f(l,r) 连通块减少的个数,而每个 uu 会对所有 lul\leq uf(l,r1)f(l,r-1) 产生贡献。

所以,我们从 11nn 枚举 rr,用线段树对所有 ll 维护 f(l,r)f(l,r)(具体地,维护区间最小值和最小值的个数,统计答案时判断最小值是否为 11),这个题就做完了。

代码

*J. Triangle City

题意

有一个三角形网格,要求从 (1,1)(1,1) 走到 (n,n)(n,n)。要求每条边只经过一次,求一条路径使得经过边的边权之和最大。

题解

把玩一下样例可以发现一种行走方案总可以由网格中刨去一条从 (1,1)(1,1)(n,n)(n,n) 的路径得到(因为此时,只有 (1,1)(1,1)(n,n)(n,n) 两个奇点,由一笔画的知识可以知道一定可以从 (1,1)(1,1) 出发走到 (n,n)(n,n) 结束)。所以要使得边权之和最大,跑出 (1,1)(1,1)(n,n)(n,n) 的一条最短路,然后将这些边删除,利用剩下的边跑一遍欧拉回路就可以了。感觉实现起来稍微有点麻烦。

代码

K. Happy Equation

题意

解方程

axxa(mod2p)a^x\equiv x^a\pmod {2^p}

其中 1x2p1\leq x\leq 2^p

数据范围:1a109,1p301\leq a\leq 10^9,1\leq p\leq 30

题解

诈骗题啊!

打表可得 aa 为奇数时答案始终为 11

aa 为偶数时:

xpx\geq p,则 ax0(mod2p)a^x\equiv 0\pmod {2^p}。只需求解 xa0(mod2p)x^a\equiv 0\pmod {2^p}。记 xx22 的次数为 kk,则 xax^a22 的次数为 kaka。原方程即要求 kapka\geq p,解得 kpak\geq \lceil\dfrac{p}{a}\rceil 。容易知道,在 [p,2p][p,2^p] 范围内,2k2^k 的倍数有 2pkp12k2^{p-k}-\lfloor\dfrac{p-1}{2^k}\rfloor 个。

对于小于 ppxx,直接暴力跑就可以了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll fpow(ll a,ll b,ll p)
{
ll res=1;
while(b)
{
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;cin>>t;
while(t--)
{
ll a,p;cin>>a>>p;
if(a&1) cout<<1<<'\n';
else
{
ll ans=0;
for(int i=1;i<p;i++)
if(fpow(a,i,(1<<p))==fpow(i,a,(1<<p))) ans++;
ll k=(p+a-1)/a;
ans+=(1<<(p-k))-(p-1)/(1<<k);
cout<<ans<<'\n';
}
}
return 0;
}

The 10th Shandong Provincial Collegiate Programming Contest
https://je3ter.github.io/2023/07/29/ACM/The 10th Shandong Provincial Collegiate Programming Contest/
作者
Je3ter
发布于
2023年7月29日
许可协议