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;
}
其他待补
posted @ 2025-06-08 21:22  明天能下雨吗  阅读(253)  评论(0)    收藏  举报