2025ccpc郑州邀请赛
这次在郑州主场作战,队友超常发挥拿下省银加邀请赛银,也是这上半年打的最好的一场了
最终拿下7题,可惜省银是银牌第二,差点拿金
以下是本人题解
M
签到题,要把一个图通过删边和加边操作变成一颗树,求最小操作次数
考虑先把每个连通块连起来,再删边直至只剩n-1条边即可
可用dfs或并查集搜出连通块个数cnt
设有n个点和m条边
最小操作次数m+(cnt-1)-(n-1)+(cnt-1)
H
队友想了半天好像想歪了,最后发现f(n-1)*f(n) = f(n^2),说明n和n+1可达,即任意两个数可达,答案就是r-l+1
D
签到题,队友打的
F
把右下角终点的连通块看成一个点,考虑求出每个方格到这个点的最短路(类似迪杰斯特拉算法的想法),最后搜索起点的连通块找到所有点的最短路径长度即可
由于是方格图,可以用bfs搜索实现最短路算法
J
签到题,暴力模拟移位即可
G
队友打的,大概是构造一个菊花图加一条链的形式
E
还是队友打的,没想到他写了10分钟左右就一发过了
考虑把所有单词建成一颗字典树,如果一个节点作为前缀出现了m次,要使其贡献最大肯定是尽可能把它平分到两种咒语中
那么它的贡献就是$ \lfloor \frac{m}{2} \rfloor * \lfloor \frac{m+1}{2} \rfloor $
把字典树的每个节点都算一遍即得答案
C
比赛时感觉能写,思路也差不多,但是没接触过珂朵莉树,码力不够,还是没能做出,不过后来补了
用珂朵莉树树维护区间覆盖操作,再用可区间修改的权值线段树维护每种宝可梦数量
每次有区间变更就在线段树里做对应区间修改(这数据结构真够复杂的)
时间复杂度\(O(nlogn)\)
点击查看代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define lowbit(x) = ((x)& - (x))
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
using namespace std;
typedef int ll;
const int N = 4e5+3;
int T,n,m,l,r,d,n2;
int a[N];
int tree[N<<2],tag[N<<2];
int ls(int p){ return p<<1; }
int rs(int p){ return p<<1|1;}
void push_up(ll p){
tree[p] = max(tree[ls(p)] , tree[rs(p)]);
}
void build(int p,int pl,int pr){
tag[p] = 0;
if(pl==pr){tree[p]=a[pl]; return;}
int mid = (pl+pr) >> 1;
build(ls(p),pl,mid);
build(rs(p),mid+1,pr);
push_up(p);
}
void addtag(ll p,ll pl,ll pr,ll d){
tag[p] += d;
tree[p] += d;
}
void push_down(ll p,ll pl,ll pr){
if(tag[p]){
ll mid = (pl+pr)>>1;
addtag(ls(p),pl,mid,tag[p]);
addtag(rs(p),mid+1,pr,tag[p]);
tag[p]=0;
}
}
void update(ll L,ll R,ll p,ll pl,ll pr,ll d){
if(L<=pl && pr<=R){
addtag(p, pl, pr,d);
return;
}
push_down(p,pl,pr);
ll mid=(pl+pr)>>1;
if(L<=mid) update(L,R,ls(p),pl,mid,d);
if(R>mid) update(L,R,rs(p),mid+1,pr,d);
push_up(p);
}
int query(int p,int pl,int pr){
if(pl == pr) return pl;
push_down(p,pl,pr);
int mid = (pl+pr)>>1;
if(tree[ls(p)] == tree[p])return query(ls(p),pl,mid);
else return query(rs(p),mid+1,pr);
}
struct node{
int l,r;
mutable int v;
node(const int &il, const int &ir, const int &iv) : l(il), r(ir), v(iv) {}
bool operator < (const node& o)const{
return l<o.l;
}
};
set<node> s;
#define IT set<node>::iterator
IT split(int pos){
IT it = s.lower_bound(node(pos,0,0));
if(it != s.end() && it->l == pos) return it;
--it;
int L = it->l,R = it->r,v = it->v;
s.erase(it);
s.insert(node(L,pos-1,v));
return s.insert(node(pos,R,v+pos-L)).first;
}
void assign(int l,int r,int val){
IT ir = split(r+1),il = split(l);
for(IT it = il;it!=ir;it++){
update(it->v,(it->v)+(it->r)-(it->l),1,1,n2,-1);
}
s.erase(il,ir);
s.insert(node(l,r,val));
update(val,val+r-l,1,1,n2,1);
return;
}
void init(){
cin>>n>>m;
n2 = n<<1;
int x;
s.clear();
rep(i,1,n2)a[i] = 0;
rep(i,1,n){
cin>>x;
a[x]++;
s.insert(node(i,i,x));
}
rep(i,1,n2){
tree[i] = 0;
tag[i] = 0;
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>T;
while(T--){
init();
build(1,1,n2);
cout<<query(1,1,n2)<<' '<<tree[1]<<'\n';
while(m--){
cin>>l>>r>>d;
assign(l,r,d);
cout<<query(1,1,n2)<<' '<<tree[1]<<'\n';
}
}
return 0;
}
B
比赛时想到dp,但是没想到最后一个数连续出现时怎么转移状态,差一点,后来补了
维护三维dp数组,\(dp_{i,j,k}\)代表前i次取出操作,最后一个取出的数为j,j连续出现k次时序列递增概率
公式有点复杂,这里直接贴题解

再开一个滚动数组和进行一个类似前缀和的优化,以及概率中常用的线性求逆元就能通过该题
时间复杂度\(O(n^2)\)
点击查看代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define lowbit(x) = ((x)& - (x))
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define xx first
#define yy second
using namespace std;
typedef long long ll;
const int N = 5e3+3;
const ll mod = 998244353;
ll t,n,a,cnt,cntq;
ll numa[N],inv[N];
ll dp[N][N],s[N];
void print(){
rep(i,0,n){
cout<<s[i]<<' ';
rep(j,0,n){
cout<<dp[i][j]<<' ';
}
cout<<'\n';
}
}
void solve(){
ll num = 0;
if(cntq == 1){
//dp[0][1] = numa[0]*inv[cnt]%mod;
rep(i,0,n){
dp[i][1] = numa[i]*inv[cnt]%mod;
s[i] = dp[i][1];
}
//print();
return;
}
rep(i,0,n){
num = (num+s[i])%mod;
}
per(i,n,0){
num = (mod+num-s[i])%mod;
if(numa[i]){
s[i] = 0;
per(j,numa[i],2){
dp[i][j] = dp[i][j-1]*(numa[i]-(j-1))%mod*inv[cnt]%mod;
s[i] = (s[i]+dp[i][j])%mod;
}
dp[i][1] = num*numa[i]%mod*inv[cnt]%mod;
s[i] = (s[i]+dp[i][1])%mod;
}
}
//print();
return;
}
void init(){
rep(i,0,n){
numa[i] = 0;
s[i] = 0;
rep(j,0,n){
dp[i][j] = 0;
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
inv[1] = 1;
rep(i,2,N-1){
inv[i] = (mod-mod/i)*inv[mod%i]%mod;
}
while(t--){
cin>>n;
cnt = 0;cntq = 0;
init();
rep(i,1,n){
cin>>a;
if(a == -1){
++cntq;
solve();
--cnt;
}
else{
numa[a]++;
++cnt;
}
}
if(cntq == 0){
cout<<1<<'\n';
continue;
}
int ans = 0;
rep(i,0,n){
rep(j,1,n){
ans = (ans+dp[i][j])%mod;
}
}
cout<<ans<<'\n';
}
return 0;
}
更新一下k题补题
K
用到了fft快速傅里叶变换求循环卷积,具体思路懒得写了,fft也是抄的板子
点击查看代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define lowbit(x) = ((x)& - (x))
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
using namespace std;
typedef long long ll;
struct Complex {
double x, y;
Complex(double _x = 0.0, double _y = 0.0) {
x = _x;
y = _y;
}
Complex operator-(const Complex &b) const {
return Complex(x - b.x, y - b.y);
}
Complex operator+(const Complex &b) const {
return Complex(x + b.x, y + b.y);
}
Complex operator*(const Complex &b) const {
return Complex(x * b.x - y * b.y, x * b.y + y * b.x);
}
};
const double PI = acos(-1.0);
const int N = 1<<20;
int n,m,a;
int h[N],rev[N];
int ncnt[10] = {
1,0,0,0,1,0,1,0,2,1
};
Complex fa[N],fb[N];
void change(Complex y[], int len) {
for (int i = 0; i < len; ++i) {
rev[i] = rev[i >> 1] >> 1;
if (i & 1) {
rev[i] |= len >> 1;
}
}
for (int i = 0; i < len; ++i) {
if (i < rev[i]) {
swap(y[i], y[rev[i]]);
}
}
return;
}
// rev=1,DFT; rev=-1,IDFT
void fft(Complex y[], int len, int on) {
change(y, len);
for (int h = 2; h <= len; h <<= 1) {
Complex wn(cos(2 * PI / h), sin(2 * PI / h));
for (int j = 0; j < len; j += h) {
Complex w(1, 0);
for (int k = j; k < j + h / 2; k++) {
Complex u = y[k];
Complex t = w * y[k + h / 2];
y[k] = u + t;
y[k + h / 2] = u - t;
w = w * wn;
}
}
}
if (on == -1) {
reverse(y + 1, y + len);
for (int i = 0; i < len; i++) {
y[i].x /= len;
y[i].y /= len;
}
}
}
int calc(int x){
if(x == 0)return 1;
int res = 0;
while(x>0){
res += ncnt[x%10];
x/=10;
}
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
int len = 1;
while(len<3*m-1)len<<=1;
rep(i,0,m-1){
fb[i] = Complex(calc(m-1-i),0);
fb[m+i] = fb[i];
}
rep(i,2*m,len)fb[i] = Complex(0,0);
rep(i,1,n){
cin>>a;
h[a]++;
}
rep(i,0,m-1)fa[i] = Complex(h[i],0);
rep(i,m,len)fa[i] = Complex(0,0);
fft(fa,len,1);
fft(fb,len,1);
//rep(i,0,len-1)cout<<fa[i].x<<' ';cout<<'\n';
//rep(i,0,len-1)cout<<fb[i].x<<' ';cout<<'\n';
rep(i,0,len-1)fa[i] = fa[i] * fb[i];
fft(fa,len,-1);
double ans = 0;
rep(i,m-1,2*m-1)ans = max(fa[i].x,ans);
cout<<(int)(ans+0.5);
return 0;
}

浙公网安备 33010602011771号