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;
}

浙公网安备 33010602011771号