2025ICPC上海区域赛A,D,J,K题

2025ICPC上海区域赛A,D,J,K题

A

思路:

我们设立一个可行集合,尽可能让每次排除一半,我们可以在可行集合内随机选一个点,然后求出可行集合其他点到该随机点的距离的平均数,这个平均数就是我们询问的key,40此绰绰有余,实测25次询问都可以稳定AC,为什么是平均数??,因为我想尽可能一次性排除多一点的数,不能让范围太大或者太小,如果是中位数可能会太大,白白浪费很多询问,而平均数就可以保持我们排除范围的稳定,读者可以画图理解一下,非常好的题目; (当然你得求一下lca为了求点对距离,这是很显然的)

核心代码:

mt19937_64 rnd(time(0));
int rndRange(int l,int r){return rnd() % (r - l + 1) + l;}
const int P = 13331;
const int N = 200005;
vector<int> e[N];
int n;
int d[N],k[N],x[N];
void qy(int x,int k){
    cout << "? " << x << " " << k << endl;
}
int sz[N],son[N],top[N],fa[N],dep[N];
void dfs1(int u,int fa1){
  fa[u] = fa1,sz[u] = 1,dep[u] = dep[fa1] + 1;
  for(auto v:e[u]){
    if(v==fa1) continue;
    dfs1(v,u);
    sz[u] += sz[v];
    if(sz[son[u]] < sz[v]) son[u] = v;
  }
}
void dfs2(int u,int t){
  top[u] = t;
  if(!son[u]) return;
  dfs2(son[u],t);
  for(auto v:e[u]){
    if(v==fa[u] || v == son[u]) continue;
    dfs2(v,v);
  }
}
int lca(int a,int b){
  while(top[a]!=top[b]){
    if(dep[top[a]] < dep[top[b]]) swap(a,b);
    a = fa[top[a]];
  }
  return dep[a] < dep[b] ? a : b;
}
int ds(int x,int y){
    return dep[x] + dep[y] - 2*dep[lca(x,y)];
}
void solve(){
    cin >> n;
    for(int i = 1;i <= n;i++){
        e[i].clear();
        fa[i] = 0;
        sz[i] = 0;
        son[i] = 0;
        dep[i] = 0;
        top[i] = 0;
    }
    int mx = 0;
    for(int i = 2;i <= n;i++){
        int fa;
        cin >> fa;
        e[fa].pb(i);
    }
    dfs1(1,0);
    dfs2(1,1);
    for(int i = 1;i <= n;i++) mx = max(mx,dep[i]);
    vector<int> vs;
    for(int i = 1;i <= n;i++) vs.pb(i);
    int cs = 40;
    while(cs-- && vs.size() > 1){
        int sz = vs.size();
        auto id = rndRange(0,sz - 1);
        int x = vs[id];
        int su = 0;
        for(auto y:vs) su += ds(x,y);
        int k = su/sz;
        qy(x,k);
        bool ok;
        cin >> ok;
        vector<int> ne;
        for(auto p:vs){
            bool tp = (ds(p,x) <= k);
            if(ok == tp) ne.pb(p); 
        }
        vs = ne;
    }
    cout << "! " << vs[0] << endl;

}

signed main(){
    // std::ios::sync_with_stdio(0);
    // std::cin.tie(0);
    // std::cout.tie(0);
    int times = 1;
    cin >> times;
    while(times--){
        solve();
    }
    return 0;
}

D

思路

类似SOSDP,高维前缀和,把 0,1,?看成三进制0,1,2,把0和1处理过的信息挂到2上,具体的dp[??00] 可以从 dp[?100] + dp[?000] 中获得,因为三进制顺序遍历,所以dp[?100]和dp[?000]这两个数据一定是我们之前处理过的,然后类似的dp[?100] 可以从dp[0100] 和dp[1100]转移,具体看代码实现,如果写的差一点的话是\(O(3^n *n)\)理论上过不去,但是加上访问到第一个?时候break的小优化QOJ和赛时就能卡过去,纯血\(O(3^n)\)是预处理每一个集合最低位?也就是2的位置

核心代码:

const int N = 200005;
int pw[N];
int n;
int a[1 << 17];
void solve(){
    cin >> n;
     int mask = 1 << n;
    for(int i = 0;i < mask;i++) cin >> a[i];
   
    pw[0] = 1;
    for(int i = 1;i <= n;i++){
        pw[i] = pw[i - 1] * 3;
    }
    vector<int> dp(pw[n] + 10,0);
    vector<int> lst(pw[n] + 10,-1);
    int ans = 0;
    for(int i = 0;i < pw[n];i++){
        if(i % 3 == 2){
            lst[i] = 0;
        }else{
            if(lst[i/3]!=-1) lst[i] = lst[i/3] + 1;
        }
    }
    for(int i = 0;i < pw[n];i++){
        
        if(lst[i]==-1){
            int t = 0;
            int val = i;
            for(int j = 0;j < 16;j++){
                if(val%3 == 1) t |= (1 << j);
                val/=3;
            }
            ans ^= a[t];
            dp[i] = a[t];
        }else{
            int t1 = pw[lst[i]];
            dp[i] = dp[i - t1] + dp[i - 2*t1];
            ans ^= dp[i];
        }
    }
    cout << ans << endl;
}

J

个人最简单的一题,感觉这才是签到....

思路:

直接点对点跑路径模拟或者搞什么优先队列复杂度很大基本上是nk的复杂度了,我们可以想成Trie那种思路,先走权值为1的边,多少条都走,然后往下面的Trie节点挂上能走1边过去的所有节点,然后dfs下去,直到1走光,如果还有k,dfs会归回来一层访问2就行了,这样看似挂点的复杂度很高,但每次挂点都会消耗一个k,实际复杂度只有k

核心代码:

#define pb push_back
int n,m,k;
vector<int> tr[N][9];
vector<int> fk[N * 20];
int idx = 0;
void dfs(int u,int len){
    for(int i = 1;i <= 8;i++){
        if(fk[u].size()){
            ++idx;
            for(auto p:fk[u]){
                for(auto v:tr[p][i]){
                    k--;
                    cout << len << endl;
                    if(k==0) return;
                    fk[idx].pb(v);
                }
            }
            dfs(idx,len + 1);
            if(k==0) return;
        }
    }
}
void solve(){
    cin >> n >> m >> k;
    for(int i = 1;i <= m;i++){
        int u,v,w;
        cin >> u >> v >> w;
        tr[u][w].pb(v);
    }
    for(int i = 1;i <= n;i++){
        fk[0].pb(i);
    }
    
    dfs(0,1);
    while(k--) cout << -1  << endl;
}

K

思路:

可以用单侧递归线段树,也可以分块,我用分块实现的,东西有点多,大致思路是先求每块的前缀最大值,前缀最小值,前缀最小值之和,前缀最大值之和,前缀最大 * 前缀最小值之和,维护两个标记,cov覆盖标记,tag增加标记,然后询问就是进块之前的mi,和mx在哪个位置更新(使用二分),把一个块分成不到三个区间,然后分类讨论一下即可. 说一点比较细节的东西,怎么动态维护 前缀最大*前缀最小的和 比如说增加进去一个tag,那么询问时候就是 设原来是 x * y, 现在变成了 (x + c) * (y + c) 拆一下可以变成 x * y + c * c + (x+y) * c, 所有值我们都存过,直接根据公式求即可,具体看代码吧

代码:

//我必须考虑这是否是我此生仅有的机会
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
template <typename T>
using Heap = __gnu_pbds::priority_queue<T, std::less<T>,pairing_heap_tag>;
template <typename T>
using RBTree = __gnu_pbds::tree<T, null_type, std::less<T>, rb_tree_tag, tree_order_statistics_node_update>;
#define int long long
#define lc u<<1
#define rc u<<1|1
#define pb push_back
#define vt vector
#define fi first
#define se second
#define all(x) x.begin(), x.end()
#define PII pair<int,int>
#define endl "\n"
#define il inline
#define DEBUG //
//undef DEBUG
#define yn(ans) cout << ((ans)?"Yes":"No") << endl;
#define YN(ans) cout << ((ans)?"YES":"NO") << endl;
typedef unsigned long long ULL;
typedef long long ll;
template <typename T>
void debug(vector<T> v){cerr << "debug-------debug"<<endl;for(auto &p:v) cerr << p << " ";cerr << endl << "debug-------debug"<<endl;}
il int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x*f;}
mt19937_64 rnd(time(0));
int rndRange(int l,int r){return rnd() % (r - l + 1) + l;}
const int inf = 1e12;
const int infi = 0x3f3f3f3f;
const int MOD = 998244353;
const int P = 13331;
const int N = 200005;
int n,q;
int a[N];
struct Block{
    int L,R,len;
    int tag,cov;
    vector<int> premi,premx;
    vector<ULL> presum;
    vector<ULL> sumi,sumx;
    vector<int> info;
    void rebuild(){
        tag = cov = 0;
        info.clear();
        premi.assign(len + 1,inf);
        premx.assign(len + 1,-inf);
        presum.assign(len + 1,0);
        sumi.assign(len + 1,0);
        sumx.assign(len + 1,0);
        info.push_back(0);
        for(int i = L;i <= R;i++) info.push_back(a[i]);
        for(int i = 1;i <= len;i++){
            premi[i] = min(premi[i - 1],info[i]);
            premx[i] = max(premx[i - 1],info[i]);
            sumi[i] = sumi[i - 1] + (ULL)premi[i];
            sumx[i] = sumx[i - 1] + (ULL)premx[i];
            presum[i] = presum[i - 1] + (ULL)premi[i] * (ULL)premx[i];
        }
    }
    void addcov(int x){
        tag = 0;
        cov = x;
    }
    void addtag(int x){
        tag += x;
    }
    void dw(){
        for(int i = 1;i <= len;i++){
            if(cov){
                info[i] = cov;
            }
            info[i] += tag;
        }
        cov = tag = 0;
    }
    void hy(){
        for(int i = L;i<= R;i++){
            a[i] = info[i - L + 1];
        }
    }
    struct node{
        int x,y;
        ULL v;
    };
    ULL get_val(int L,int R){
        if(L > R){
            return 0;
        }
        ULL ans = presum[R] - presum[L - 1];
        ULL pmi = sumi[R] - sumi[L - 1];
        ULL pmx = sumx[R] - sumx[L - 1];
        ULL d = (ULL)tag;
        return ans + (pmi + pmx) * d + d * d * (ULL)(R - L + 1);
    }
    int get_mival(int L,int R){
        if(L > R){
            return 0;
        }
        int ans = (int)(sumi[R] - sumi[L - 1]);
        return ans + tag * (R - L + 1);
    }
    int get_mxval(int L,int R){
        if(L > R){
            return 0;
        }
        int ans = (int)(sumx[R] - sumx[L - 1]);
        return ans + tag * (R - L + 1);
    }
    ULL calc(int L,int R,int mi,int mx,int milen,int mxlen){
        if(L > R) return 0;
        bool usmi = (R <= milen);
        bool usmx = (R <= mxlen);
        int curlen = R - L + 1;
        if(usmi && usmx){
            return (ULL)mi * (ULL)mx * (ULL)curlen;
        }
        if(usmi && !usmx){
            return (ULL)mi * (ULL)get_mxval(L,R);
        }
        if(!usmi && usmx){
            return (ULL)mx * (ULL)get_mival(L,R);
        }
        return get_val(L,R);
    }
    node qy(int mi,int mx){
        if(cov){
            int cur = cov + tag;
            mi = min(mi,cur);
            mx = max(mx,cur);
            ULL val = (ULL)mi * (ULL)mx * (ULL)len;
            return {mi,mx,val};
        }
        int milen = 0; //找一个比mi小的数字位置-1
        {
            int l = 1,r = len;
            while(l <= r){
                int mid = l + r >> 1;
                if(premi[mid] + tag > mi){
                    milen = mid;
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
        }
        int mxlen = 0; //找一个比mx大的数字位置-1
        {
            int l = 1,r = len;
            while(l <= r){
                int mid = l + r >> 1;
                if(premx[mid] + tag < mx){
                    mxlen = mid;
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
        }
        ULL ans = 0;
        vector<int> qj = {0,milen,mxlen,len};
        sort(all(qj));
        qj.erase(unique(all(qj)),qj.end());
        for(int i = 0;i < qj.size() - 1;i++){
            int L = qj[i] + 1,R = qj[i + 1];
            ans += calc(L,R,mi,mx,milen,mxlen);
        }
        return {min(mi,premi[len] + tag),max(mx,premx[len] + tag),ans};
    }
};
struct node{
    int B,num;
    vector<Block> bs;
    vector<int> be;
    void init(int n){
        B = sqrt(n);
        num = (n + B - 1)/B;
        bs.resize(num + 1);
        be.resize(n + 1);
        bs[0].R = 0;
        for(int i = 1;i <= num;i++){
           bs[i].L = bs[i-1].R + 1;
           bs[i].R = min(bs[i].L + B - 1,n);
           bs[i].len = bs[i].R - bs[i].L + 1;
           bs[i].rebuild();
           for(int j = bs[i].L;j <= bs[i].R;j++) be[j] = i;
        }
    }
    void add(int L,int R,int v){
        if(be[L] == be[R]){
            int x = be[L];
            bs[x].dw();
            bs[x].hy();
            for(int i = L;i <= R;i++){
                a[i] += v;
            }
            bs[x].rebuild();
            return;
        }
        //left
        int x = be[L];
        bs[x].dw(),bs[x].hy();
        for(int i = L;i <= bs[x].R;i++){
            a[i] += v;
        }
        bs[x].rebuild();
        //right
        x = be[R];
        bs[x].dw(),bs[x].hy();
        for(int i = bs[x].L;i <= R;i++){
            a[i] += v;
        }
        bs[x].rebuild();
        //zj
        for(int i = be[L] + 1;i <= be[R] - 1;i++){
            bs[i].addtag(v);
        }
    }
    void cov(int L,int R,int v){
        if(be[L] == be[R]){
            int x = be[L];
            bs[x].dw();
            bs[x].hy();
            for(int i = L;i <= R;i++){
                a[i] = v;
            }
            bs[x].rebuild();
            return;
        }
        //left
        int x = be[L];
        bs[x].dw(),bs[x].hy();
        for(int i = L;i <= bs[x].R;i++){
            a[i] = v;
        }
        bs[x].rebuild();
        //right
        x = be[R];
        bs[x].dw(),bs[x].hy();
        for(int i = bs[x].L;i <= R;i++){
            a[i] = v;
        }
        bs[x].rebuild();
        //zj
        for(int i = be[L] + 1;i <= be[R] - 1;i++){
            bs[i].addcov(v);
        }
    }
    ULL qy(int L,int R){
        int mi = inf,mx = -inf;
        ULL ans = 0;
        if(be[L] == be[R]){
            int x = be[L];
            bs[x].dw();
            bs[x].hy();
            bs[x].rebuild();
            for(int i = L;i <= R;i++){
                mi = min(mi,a[i]);
                mx = max(mx,a[i]);
                ans += (ULL)mi * (ULL)mx;
            }
            return ans;
        }
        int x = be[L];
        bs[x].dw();
        bs[x].hy();
        for(int i = L;i <= bs[x].R;i++){
            mi = min(mi,a[i]);
            mx = max(mx,a[i]);
            ans += (ULL)mi * (ULL)mx;
        }
        bs[x].rebuild();
        for(int i = be[L] + 1;i <= be[R] - 1;i++){
            auto [x1,y1,v] = bs[i].qy(mi,mx);
            mi = min(x1,mi);
            mx = max(y1,mx);
            ans += v;
        }
        x = be[R];
        bs[x].dw();
        bs[x].hy();
        for(int i = bs[x].L;i <= R;i++){
            mi = min(mi,a[i]);
            mx = max(mx,a[i]);
            ans += (ULL)mi * (ULL)mx;
        }
        bs[x].rebuild();
        return ans;
    }
}fk;

void solve(){
    cin >> n >> q;

    for(int i = 1;i <= n;i++){
        cin >> a[i];
    }
    fk.init(n);
    // fk.cov(1,5,10);
    // for(int i = 1;i <= n;i++){
    //     cout << a[i] << " ";
    // }
    while(q--){
        int op,l,r;
        cin >> op >> l >> r;
        if(op == 1){
            int v;
            cin >> v;
            fk.add(l,r,v);
        }else if(op == 2){
            int v;
            cin >> v;
            fk.cov(l,r,v);
        }else{
            cout << fk.qy(l,r) << endl;
        }
    }
    // for(int i = 1;i <= n;i++){
    //     cout << a[i] << " ";
    // }
}

signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    int times = 1;
    //cin >> times;
    while(times--){
        solve();
    }
    return 0;
}
posted @ 2026-09-26 10:34  GsGXT  阅读(7)  评论(0)    收藏  举报