24暑假赛训合集
本文同步自 24暑期赛训合集
谢谢,你关注的鸽子博主更新了。
上赛季末段没能忍住网瘾, 转生成 ACMer 了
和队友一起拿了块邀请赛金牌和省赛冠军,下半年区域赛不想拖后腿所以还是得努努力啊。
但是因为博主还要跑科研实验 以及 机器人比赛的事情,所以大概一天只能看几个题
唉,被自己菜晕,能不能来点作用,别浪费队友开出来的题。
下列列出的 √ 为自己想出来的,× 为看了题解。
个人训练赛记录,CF / AT 为主
比赛 | sloved problem | rank |
---|---|---|
Codeforces Round #956 (Div. 2) and ByteRace 2024 | 4/7 | 802 |
Codeforces Round 958 (Div. 2) | 3/6 | 1190 |
Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2) | 5/8 | 806 |
Codeforces Round 960 (Div. 2) | 4/7 | 368 |
wanna_be_free 训练赛记录,排名以 vp 榜为主
比赛 | sloved problem | rank |
---|---|---|
2024 (ICPC) Jiangxi Provincial Contest | 10/12 | 10 |
The 2024 CCPC National Invitational Contest (Northeast), The 18th Northeast Collegiate Programming Contest | 12/13 | 5 |
2024 ICPC National Invitational Collegiate Programming Contest, Wuhan Site sloved problem(这场暂时先不写记录了吧,来不及写完了,回来补这场的时候再写) | 6/13 | 106 |
24牛客多校第一场 | 7/11 | 14 |
24牛客多校第二场 | 7/10 | 40 |
24牛客多校第三场 | 6/12 | 70 |
24牛客多校第四场 | 9/12 | 35 |
24牛客多校第五场 | 5/13 | 49 |
24牛客多校第六场 | 7/11 | 32 |
24牛客多校第七场 | 5/12 | 27 |
24牛客多校第八场 | 5/11 | 82 |
[√] 不知名题
求
看这个
思考
所以直接就倍增即可。
//明剑照霜,秋风走马
#include<bits/stdc++.h>
#define ll long long
#define M 600005
ll mod,a,b,c;
int T;
int A[M * 2][80];
ll mul[80];
signed main(){
// freopen("5.in","r",stdin);
// freopen("5.out","w",stdout);
scanf("%lld%lld%lld%lld",&mod,&a,&b,&c);
a = a % mod;
b = b % mod;
c = c % mod;
mul[0] = 1;
for(int i = 1;i <= 60;++i)mul[i] = mul[i - 1] * 2;
for(int i = 0;i <= 2 * mod;++i){A[i][0] = (1ll * a * (i % mod) * (i % mod) % mod+ 1ll * b * (i % mod) % mod + c) % mod + i / 2;}
for(int t = 1;t <= 60;++t){
for(int i = 0;i <= 2 * mod;++i){
A[i][t] = A[A[i][t - 1]][t - 1];
}
}
scanf("%lld",&T);
while(T -- ){
ll k,y;
scanf("%lld%lld",&k,&y);
while((k > 2 * mod) && y){--y;k = (1ll * a * (k % mod) * (k % mod) % mod + 1ll * b * (k % mod) % mod + c) % mod + k / 2;}
if(y == 0){std::cout<<k<<"\n";continue;}
ll now = k;
for(int t = 60;t >= 0;--t){if(y < 0)return 0;if(y >= mul[t]){now = A[now][t];y -= mul[t];}if(!y)break;}
std::cout<<now<<"\n";
}
}
另外 ll 求余 int 居然是 UB 吗,,,,
[√] [Usaco2008 Jan]猜数游戏
给若干区间
问最多前几个区间能够在数组每个都不一样的条件下自洽
首先第一眼看出来了能不能判全局合法,考虑每个相同权值的区间求交,由于每个数不一样则这个最小值一定出现在交集内,若没有交集则错
然后考虑从大权值加到小权值,若一个小权值的区间,在之前已经被大权值区间全部覆盖,则这个区间无法填数 (因为大权值限制条件显然更强)
然后想错了,上来写了个吉司机,一个区间一个区间加入,获得了 6.0 / 100.0 的好成绩
然后既然能判全局合法的方法,那我们直接二分 M 即可。
//山桃红花满上头,蜀江春水拍山流。
#include<bits/stdc++.h>
#define N 2000005
int t[N << 2];
int sum[N << 2];
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define tag(x) t[x]
#define s(x) sum[x]
#define mid ((l + r) >> 1)
int n,q;
#define root 1,1,n
inline void push(int u,int l,int r){if(tag(u) == -1)return ;tag(ls(u)) = tag(rs(u)) = tag(u);s(u) = (r - l + 1) * tag(u);s(ls(u)) = (mid - l + 1) * tag(u);s(rs(u)) = (r - mid) * tag(u);tag(u) = -1;}
inline void up(int u){s(u) = s(ls(u)) + s(rs(u));}
inline void clear(int u,int l,int r){
tag(u) = -1;s(u) = 0;
clear(ls(u),l,mid);clear(rs(u),mid + 1,r);
}
inline void cover(int u,int l,int r,int lt,int rt,int p){
// std::cout<<"COVER "<<u<<" "<<l<<" "<<r<<" "<<lt<<" "<<rt<<" "<<p<<"\n";
if(lt <= l && r <= rt){tag(u) = p;s(u) = (r - l + 1) * tag(u);return ;}
push(u,l,r);
if(lt <= mid)cover(ls(u),l,mid,lt,rt,p);
if(rt > mid)cover(rs(u),mid + 1,r,lt,rt,p);
up(u);
}
inline int query(int u,int l,int r,int lt,int rt){
int ans = 0;
if(lt <= l && r <= rt){return s(u);}
push(u,l,r);
if(lt <= mid)ans += query(ls(u),l,mid,lt,rt);
if(rt > mid)ans += query(rs(u),mid + 1,r,lt,rt);
return ans;
}
struct P{int l,r,v;}A[N],p[N];
using std::map;
map<int,int>L,R;
bool operator < (P A,P B){return A.v > B.v;}
inline bool check(int lim){
for(int i = 1;i <= lim;++i)p[i] = A[i];
cover(root,1,n,0);
L.clear();R.clear();
std::sort(p + 1,p + lim + 1);
// std::cout<<lim<<"\n";
// for(int i = 1;i <= lim;++i)std::cout<<p[i].l<<" "<<p[i].r<<" "<<p[i].v<<"\n";
int las = 1;
for(int i = 1;i <= lim;++i){
//judge and
if(p[i].v != p[i - 1].v){
while(p[las].v != p[i].v){
cover(root,p[las].l,p[las].r,1);
++ las;
}
}
if(L[p[i].v] == 0){L[p[i].v] = p[i].l;R[p[i].v] = p[i].r;}
else {L[p[i].v] = std::max(L[p[i].v],p[i].l);R[p[i].v] = std::min(R[p[i].v],p[i].r);}
if(R[p[i].v] < L[p[i].v]){return 0;}
//judge cover
// std::cout<<"QUERY "<<L[p[i].v]<<" "<<R[p[i].v]<<"\n"<<query(root,L[p[i].v],R[p[i].v])<<"\n";
if(query(root,L[p[i].v],R[p[i].v]) == (R[p[i].v] - L[p[i].v] + 1))return 0;
}
return 1;
}
int main(){
scanf("%d%d",&n,&q);
for(int i = 1;i <= q;++i){scanf("%d%d%d",&A[i].l,&A[i].r,&A[i].v);}
int l = 1,r = q;
while(l + 1 < r){
// std::cout<<l<<" "<<r<<" "<<check(mid)<<"\n";
if(check(mid)){l = mid;}
else r = mid - 1;
}
int ans;
if(check(r))ans = r;
else if(check(l))ans = l;
if(check(q))ans = q;
if(ans == q)puts("0");else std::cout<<ans + 1<<"\n";
}
/*
20 4
1 10 7
5 19 7
3 12 8
11 15 12
*/
[x] k-Maximum Subsequence Sum
每次单点改
询问一个区间上
赛场上完全没懂,下来一看原来以前做过这个题,果然以前训练也太抽象了。
考虑使用反悔贪心的操作,每次选取一个区间最大子段和,然后把这一段取反,再取最大子段和,重复
然后代码不是很好写,但也还行,就是比较麻烦,中间被没预设取反标志值坑了很久.
//山桃红花满上头,蜀江春水拍山流。
#include<bits/stdc++.h>
#define N 100005
struct seg{
int l,r,s;
seg(int l = 0,int r = 0,int s = 0):l(l),r(r),s(s){}
};
seg operator + (seg A,seg B){return seg(A.l,B.r,A.s + B.s);}
bool operator < (seg A,seg B){return (A.s < B.s);}
struct node{
seg lmi,lmx;
seg rmi,rmx;
seg mi,mx;
seg all;
bool rev;
}T[N << 2];
#define lmi(x) x.lmi
#define lmx(x) x.lmx
#define rmi(x) x.rmi
#define rmx(x) x.rmx
#define mx(x) x.mx
#define mi(x) x.mi
#define all(x) x.all
#define tag(x) x.rev
#define L(x) x.l
#define R(x) x.r
#define S(x) x.s
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
inline void init(node &x,int t,int p){
L(lmi(x)) = L(lmx(x)) = L(rmi(x)) = L(rmx(x)) = L(all(x)) = L(mi(x)) = L(mx(x)) = t;
R(lmi(x)) = R(lmx(x)) = R(rmi(x)) = R(rmx(x)) = R(all(x)) = R(mi(x)) = R(mx(x)) = t;
S(lmi(x)) = S(lmx(x)) = S(rmi(x)) = S(rmx(x)) = S(all(x)) = S(mi(x)) = S(mx(x)) = p;
tag(x) = 0;
}
node operator + (node A,node B){
node x;
lmi(x) = std::min(lmi(A),all(A) + lmi(B));
lmx(x) = std::max(lmx(A),all(A) + lmx(B));
rmi(x) = std::min(rmi(B),rmi(A) + all(B));
rmx(x) = std::max(rmx(B),rmx(A) + all(B));
mi(x) = std::min(rmi(A) + lmi(B),std::min(mi(A),mi(B)));
mx(x) = std::max(rmx(A) + lmx(B),std::max(mx(A),mx(B)));
all(x) = all(A) + all(B);
tag(x) = 0;
return x;
}
inline void prind(seg U){printf("[%d,%d],sum: %d\n",L(U),R(U),S(U));}
inline void print(node U,int l,int r){
puts("----------------------------");
printf("THE BLOCK OVER WITH [%d,%d] : \n",l,r);
printf("THE LMI:");prind(lmi(U));
printf("THE LMX:");prind(lmx(U));
printf("THE RMI:");prind(rmi(U));
printf("THE RMX:");prind(rmx(U));
printf("THE MI:");prind(mi(U));
printf("THE MX:");prind(mx(U));
printf("THE ALL:");prind(all(U));
puts("----------------------------");
}
inline void flip(node &x){
tag(x) ^= 1;
std::swap(lmi(x),lmx(x));std::swap(rmi(x),rmx(x));std::swap(mi(x),mx(x));
S(lmi(x)) *= -1;S(lmx(x)) *= -1;S(rmi(x)) *= -1;S(rmx(x)) *= -1;
S(mi(x)) *= -1;S(mx(x)) *= -1;
S(all(x)) *= -1;
}
inline void push(int u){
if(tag(T[u])){
flip(T[ls(u)]);
flip(T[rs(u)]);
tag(T[u]) = 0;
}
}
int n;
int a[N];
#define root 1,1,n
#define mid ((l + r) >> 1)
inline void build(int u,int l,int r){
if(l == r){init(T[u],l,a[l]);return ;}
build(ls(u),l,mid);build(rs(u),mid + 1,r);
T[u] = T[ls(u)] + T[rs(u)];
tag(T[u]) = 0;
}
inline void change(int u,int l,int r,int t,int p){
if(l == r){init(T[u],t,p);return ;}
push(u);
if(t <= mid)change(ls(u),l,mid,t,p);
if(t > mid)change(rs(u),mid + 1,r,t,p);
T[u] = T[ls(u)] + T[rs(u)];
}
inline void cover(int u,int l,int r,int tl,int tr){
if(tl <= l && r <= tr){
flip(T[u]);
return ;
}
push(u);
if(tl <= mid)cover(ls(u),l,mid,tl,tr);
if(tr > mid)cover(rs(u),mid + 1,r,tl,tr);
T[u] = T[ls(u)] + T[rs(u)];
}
inline node query(int u,int l,int r,int tl,int tr){
node ansA,ansB;
L(lmi(ansA)) = L(lmi(ansB)) = -1;
if(tl <= l && r <= tr)return T[u];
if(tl <= mid)ansA = query(ls(u),l,mid,tl,tr);
if(tr > mid)ansB = query(rs(u),mid + 1,r,tl,tr);
if(L(lmi(ansA)) == -1)return ansB;
if(L(lmi(ansB)) == -1)return ansA;
return ansA + ansB;
}
int q;
using std::queue;
std::queue<seg>U;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
build(root);
scanf("%d",&q);
while(q --){
int op;
scanf("%d",&op);
if(op == 0){int t,x;scanf("%d%d",&t,&x);change(root,t,x);}
if(op == 1){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
int ans = 0;
for(int i = 1;i <= k;++i){
node segment = query(root,l,r);
if(S(mx(segment)) > 0){
ans += S(mx(segment));
cover(root,L(mx(segment)),R(mx(segment)));
U.push(mx(segment));
}
}
std::cout<<ans<<"\n";
while(U.size()){
seg top = U.front();
U.pop();
cover(root,L(top),R(top));
}
}
}
}
2024 (ICPC) Jiangxi Provincial Contest
24-7-5 12:00 - 17:00
下面指的是我和队友一起做的情况
[√]A
a + b + c
[√]C
发现如果加起来刚好等于
[√]D
gcd 是质因数次数取 min
lcm 是质因数次数取 max
死去的离散数学整除格正在攻击我
考虑实际上是把每一维质因数的次数排序。
因为
质因数分解然后硬来就行了。
[√]G
队友写的,这里看看题解做法。
考虑每一位的取模贡献
又因为
所以就是加起来取模
[√]H
什么机器学习题
考虑卷积核上每一位的贡献在原矩阵上就是一个矩阵和,使用二维前缀和算出每一位贡献即可。
[√]J
麻将模拟题。
原本以为要判很多牌型,结果只有国士无双十三面和七对子。
队友看懂题之后就直接过了。
[√]K
考虑实际上等价于
[√]L
考虑
[√]F
队友上来就toptree直接写了。
非常的牛。
我只会那个线段树分治加并查集的 2log 做法,但是鉴于我队友看起来是数据结构机器人,感觉很难有他不会的数据结构题,我就不写了。
[√]I
考虑到覆盖边其实是无所谓的条件。
转化成覆盖点。
然后考虑枚举所有的外接圆,一个圆可以由
然后考虑一个圆内能覆盖多少个点,然后统计答案即可。
[x]E
给定一个序列,求找出两个不同的子序列,要求两个子序列和相同,给出方案。
非常苦手,队友上来说应该是什么随机题,非常神秘。
首先知道一个长度
其值域
那在
这说明如果一个序列长度大于等于
那么我们实际上只需要处理
接下来我们考虑如何处理
我们可以使用 mid in mid 的做法,我们处理前十五个的情况和后十五个的情况
容易想到的是我们直接就地使用一个 map,然后每个 map 的下标为 子序列A的和 与 子序列B的和 的差值,然后存的是对应一个状压的三进制数,其中每一位0/1/2表示这个元素没动/在A/在B。
那么我们在 mid in mid 最后统计结果,就是前十五个的 A - B 的差值是后十五个某方案的 A - B 差值的相反数,然后统计方案即可。
但是这样的复杂度大抵是
有点通过不了。
这里其实有很多种处理方法,后面会提到,先介绍题目所说的三个序列归并的方法。
我们考虑我们在搜索状压的时候,做的其实是二元状态
我们从前
实际上是
分别对应上述三种加入方案
那么我们只要在加入到第
你可以直接把三个序列先分别求出来再排序,也可以直接维护三个头指针,三指针移动的把三种对应的最小的先放进
然后我们得到了前十五个的状态,和后十五的状态,由于均有序,我们直接就双指针查有没有相反数即可。
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 1000000
#define M 43046721
int T;
int a[N];
int n;
struct P{
int s_val;int dval;
P(int sv = 0,int dv = 0):s_val(),dval(){};
};
bool operator < (P A,P B){return A.dval < B.dval;}
bool operator == (P A,P B){return A.s_val == B.s_val && A.dval == B.dval;}
void init(P &A,int sv,int dv){A.s_val = sv;A.dval = dv;}
using std::vector;
vector<P>S[2];
/*
0 contain A - B
1 contain B - A
*/
#define inf 5000000000
inline void print(int len,int ind){
for(auto [s_val,dval] : S[ind]){
for(int i = 0;i < len;++i){std::cout<<s_val % 3<<" ";s_val /= 3;}
std::cout<<"val :"<<dval<<"\n";
}
}
inline void del(int l,int r,int ind){//del with [l,r]
int len = r - l + 1;
S[ind].clear();
S[ind].emplace_back(0,0);
for(int i = l;i <= r;++i){
int tnull = 0,tA = 0,tB = 0;
vector<P>ans;
while(tnull < S[ind].size() || tA < S[ind].size() || tB < S[ind].size()){
P cnull,cA,cB;
if(tnull < S[ind].size())init(cnull,S[ind][tnull].s_val * 3,S[ind][tnull].dval);else init(cnull,0,inf);
if(tA < S[ind].size())init(cA,S[ind][tA].s_val * 3 + 1,S[ind][tA].dval + (ind == 0 ? 1 : -1) * a[i]);else init(cA,0,inf);
if(tB < S[ind].size())init(cB,S[ind][tB].s_val * 3 + 2,S[ind][tB].dval + (ind == 0 ? -1 : 1) * a[i]);else init(cB,0,inf);
P minn = std::min(cnull,std::min(cA,cB));
if(cnull == minn){ans.push_back(cnull);tnull ++ ;}
if(cA == minn){ans.push_back(cA);tA ++ ;}
if(cB == minn){ans.push_back(cB);tB ++ ;}
}
S[ind] = ans;
}
// print(len,ind);
}
vector<int>ans[3];
inline void find(int l,int r,int St){
for(int i = r;i >= l;--i){
ans[St % 3].push_back(i);
St /= 3;
}
}
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int i = 1;i <= n;++i){scanf("%d",&a[i]);}
if(n == 1){puts("-1");continue;}
// puts("Yes");
del(1,std::min(15,n / 2),0);del(std::min(16,n / 2 + 1),std::min(30,n),1);
int tpre = 0,tend = 0;
bool flg = 0;
ans[0].clear();ans[1].clear(),ans[2].clear();
while(tpre < S[0].size() && tend < S[1].size()){
if(S[0][tpre].dval == S[1][tend].dval && S[0][tpre].s_val != 0){
flg = 1;
find(1,std::min(15,n / 2),S[0][tpre].s_val);
find(std::min(16,n / 2 + 1),std::min(30,n),S[1][tend].s_val);
break;
}
if(S[0][tpre] == std::min(S[0][tpre],S[1][tend]))tpre ++ ;else tend++;
}
if(!flg)puts("-1");
if(flg){
std::cout<<ans[1].size()<<" ";
for(auto v : ans[1])std::cout<<v<<" ";
puts("");
std::cout<<ans[2].size()<<" ";
for(auto v : ans[2])std::cout<<v<<" ";
puts("");
}
}
}
[√]忘情
求给定一序列,将其分为
容易推导到代价实际为
如果没有
即存在一条斜率为
由于要
接着考虑如何强制选
这是一个经典的条件,我们考虑
我们考虑二分凸包斜率,找到
然后这个斜率我们以
然后我们返回可以分的段数
这里需要注意可能同一斜率上有多个点,这里应该返回最小的段数/最大的段数,后面二分自洽即可,最后的答案减去
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 100005
int n,m;
int a[N];
ll pre[N];
int minn = 2000;
ll f[N];
int t[N];
struct P{
ll x;ll y;int time;
P(ll x_val = 0,ll y_val = 0,int t = 0):x(x_val),y(y_val),time(t){};
};
P s[N];
int top,end;
inline void print(P A){std::cout<<"( "<<A.x<<" "<<A.y<<" "<<A.time<<")"<<"\n";}
inline int check(ll d){
top = end = 1;
s[end] = P(-1,1 + d);
for(int i = 1;i <= n;++i)f[i] = 0;
for(int i = 1;i <= n;++i){
while((end > top) && (2 * pre[i] * (s[top + 1].x - s[top].x) > (s[top + 1].y - s[top].y)))++top;
f[i] = s[top].y - 2 * pre[i] * s[top].x + pre[i] * pre[i];
t[i] = s[top].time + 1;
P now(pre[i] - 1,(pre[i] - 1) * (pre[i] - 1) + d + f[i],t[i]);
while(end > top && (now.y - s[end].y) * (s[end].x - s[end - 1].x) < (s[end].y - s[end - 1].y) * (now.x - s[end].x))--end;
s[++end] = now;
}
return t[n];
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i){scanf("%d",&a[i]);pre[i] = pre[i - 1] + a[i];minn = std::min(a[i],minn);}
ll l = 0,r = 1e18;
#define mid ((l + r) >> 1)
while(l + 1 < r){if(check(mid) > m)l = mid;else r = mid;}
ll ansd = 0;
if(check(l) <= m)ansd = l;
else if(check(r) <= m)ansd = r;
int used = check(ansd);
std::cout<<f[n] - 1ll * m * ansd<<"\n";
}
[√]CF1499F Diameter Cuts
给定一颗
令
特殊的
考虑如何统计答案,实际上就是任两子树内最长路径相加不大于
考虑如果显然子树内不能存在两条大于
若不存在大于
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 5005
#define mod 998244353
int f[N][N];// n longest n
int pre[N][N];// sum of pre
int fa[N];
int ps[N];
using std::vector;
vector<int>G[N];
int n,k;
inline int qpow(int x,int t){
int a = x;
int ans = 1;
while(t){
if(t & 1)ans = 1ll * ans * a % mod;
a = (1ll * a * a) % mod;
t >>= 1;
}
return ans;
}
inline int inv(int x){return qpow(x,mod - 2);}
inline void dfs(int u = 1,int ffa = 0){
fa[u] = ffa;
for(auto v : G[u]){
if(v == fa[u])continue;
dfs(v,u);
}
}
inline void del(int u = 1){
for(int i = 0;i <= k;++i)ps[i] = 1;
for(auto v : G[u]){
if(v == fa[u])continue;
del(v);
}
for(int i = 0;i <= k;++i)ps[i] = 1;
for(auto v : G[u]){
if(v == fa[u])continue;
for(int i = 0;i <= k;++i)ps[i] = 1ll * ps[i] * pre[v][i] % mod;
}
/*connected*/
for(int i = 0;i <= k / 2;++i)f[u][i + 1] = (ps[i] - (i != 0 ? ps[i - 1] : 0) + mod) % mod;
/*unconneted*/
f[u][0] = 0;
for(auto v : G[u]){
if(v == fa[u])continue;
for(int i = k / 2 + 1;i <= k;++i){
int now = 1ll * f[v][i] * ps[k - i] % mod * inv(pre[v][k - i]) % mod;
f[u][0] = (1ll * f[u][0] + now) % mod;
f[u][i + 1] = (1ll * f[u][i + 1] + now) % mod;
}
}
f[u][0] = (f[u][0] + ps[k / 2]) % mod;
pre[u][0] = f[u][0];
for(int i = 1;i <= k + 1;++i)pre[u][i] = (pre[u][i - 1] + f[u][i]) % mod;
}
int main(){
scanf("%d%d",&n,&k);
for(int i = 1;i < n;++i){
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs();
del();
std::cout<<f[1][0]<<"\n";
}
Codeforces Round #956 (Div. 2) and ByteRace 2024
[√]A
构造一个
直接输出
[√]B
给定两矩阵
考虑到实际上每行每列的对
充分条件可以考虑每行调整即可。
[√]C
给定
考虑枚举中间那段在哪,然后双指针即可,考虑显然是前一段为
判断是否满足条件即可
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
int n;
int a[N][3];
ll prea[N][3];
ll enda[N][3];
ll tot;
int T;
int ans[2][3];
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int j = 0;j <= 2;++j)
for(int i = 1;i <= n;++i)scanf("%d",&a[i][j]);
for(int j = 0;j <= 2;++j)
for(int i = 0;i <= n + 1;++i)prea[i][j] = enda[i][j] = 0;
for(int j = 0;j <= 2;++j)
for(int i = 1;i <= n;++i)prea[i][j] = prea[i - 1][j] + a[i][j];
for(int j = 0;j <= 2;++j)
for(int i = n;i >= 1;--i)enda[i][j] = enda[i + 1][j] + a[i][j];
bool flg = 0;
ll target = std::ceil(1.0 * prea[n][0] / 3);
// std::cout<<target<<"\n";
for(int j = 0;j <= 2;++j){
// std::cout<<"check "<<j<<"\n";
int t1 = (j + 1) % 3,t2 = (j + 2) % 3;
int ta = 1;
for(int i = 2;i <= n;++i){
while(prea[i][j] - prea[ta][j] >= target)ta ++ ;
// std::cout<<ta<<" "<<i<<"\n";
// std::cout<<t1<<" "<<prea[ta - 1][t1]<<" "<<t2<<" "<<enda[i + 1][t2]<<"\n";
if(prea[ta - 1][t1] >= target && enda[i + 1][t2] >= target){
ans[0][t1] = 1,ans[1][t1] = ta - 1;
ans[0][j] = ta,ans[1][j] = i;
ans[0][t2] = i + 1,ans[1][t2] = n;
flg = 1;
break;
}
std::swap(t1,t2);
// std::cout<<t1<<" "<<prea[ta - 1][t1]<<" "<<t2<<" "<<enda[i + 1][t2]<<"\n";
if(prea[ta - 1][t1] >= target && enda[i + 1][t2] >= target){
ans[0][t1] = 1,ans[1][t1] = ta - 1;
ans[0][j] = ta,ans[1][j] = i;
ans[0][t2] = i + 1,ans[1][t2] = n;
flg = 1;
break;
}
}
if(flg)break;
}
if(flg){
for(int i = 0;i <= 2;++i)
for(int j = 0;j <= 1;++j)std::cout<<ans[j][i]<<" ";
puts("");
}else puts("-1");
}
}
[√]D
给定两个序列
问能否最后相同。
显然权值集合要相同
然后跨越
所以充要条件是
考虑充分条件构造,直接把一个调整到
[√]F
给定一个序列
问区间代价第
先二分
然后考虑如何计算
我们考虑枚举子点对的右端点
考虑如何统计所有的数量,你显然可以
我们考虑可以扫描线,我们双指针维护前缀最大的
只有
使用 01-tire 来进行查询过程,两支
也有一个
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 100005
#define inf 2000000000
#define int ll
using std::multiset;
int T;
int n,k;
int a[N];
int A[N * 50][2];
int siz[N * 50];
int cnt = 1;
inline void insert(int x){
int rt = 1;
for(int i = 31;i >= 0;--i){
siz[rt] ++ ;
int t = ((x >> i) & 1);
if(!A[rt][t])A[rt][t] = ++cnt;
rt = A[rt][t];
}
siz[rt] ++ ;
}
inline void erase(int x){
int rt = 1;
if(x == -1)return ;
for(int i = 31;i >= 0;--i){
siz[rt] -- ;
int t = ((x >> i) & 1);
if(!A[rt][t])A[rt][t] = ++cnt;
rt = A[rt][t];
}
siz[rt] -- ;
}
inline int find(int x){
int rt = 1;
int ans = 0;
if(siz[rt] == 0)return inf;
for(int i = 31;i >= 0;--i){
int t = ((x >> i) & 1);
if(siz[A[rt][t]])rt = A[rt][t];
else rt = A[rt][t ^ 1],ans |= (1ll << i);
}
return ans;
}
int lasl[N];
inline int check(int x){
int l = 0;
int ans = 0;
a[0] = -1;
for(int i = 1;i <= n;++i){
bool flg = (find(a[i]) <= x);
if(flg){
while(find(a[i]) <= x){erase(a[l ++ ]);}
insert(a[-- l]);
ans = ans + (l - lasl[i - 1]) * (n - i + 1);
}
lasl[i] = l;
insert(a[i]);
}
while(l <= n){erase(a[l ++ ]);}
return ans;
}
inline void slove(){
int l = 0,r = inf;
#define mid ((l + r) >> 1)
while(l + 1 < r){
if(check(mid) >= k)r = mid;
else l = mid + 1;
}
if(check(l) >= k)std::cout<<l<<"\n";
else std::cout<<r<<"\n";
}
signed main(){
scanf("%lld",&T);
while(T -- ){
scanf("%lld%lld",&n,&k);
for(int i = 1;i <= n;++i)scanf("%lld",&a[i]);
slove();
}
}
[x]E
我们只先计算先手球的期望。
首先思考到所有的特殊球和普通球的贡献是分开的
然后普通球的贡献是好计算的,
然后思考特殊球的贡献,我当时以为是分成两段,但是后来看来是用
The 2024 CCPC National Invitational Contest (Northeast), The 18th Northeast Collegiate Programming Contest
[√]A
考虑根号操作只有
那么考虑先对
[√]D
队友发现答案一直是lose,输出
[√]E
要求
考虑反过来
[√]M
考虑如何不重复的计这个,考虑枚举中间按两个点,然后尝试枚举下面矩形点对,然后正常统计即可。队友写了各种叉积判断避免了精度问题。
[√]H
给定
考虑二分答案,对于一个点对的方案形为
考虑其为
[√]I
考虑最后一个完整排列的
其中
这个考虑容斥即可
[√]K
考虑一层层的从右到左的放置
下一层查询在上一层的左端点和其离的最近的
[√]L
考虑先取所有最小单位
[√]F
若
对
考虑直接
[√]G
考虑对出现次数根号分治即可
含有出现次数大的那边离线下来做,小的直接暴力。
[√] B
实际上每一层都实际上依赖于第二层
假设二级一开始是工作状态,则一级一开始是不工作状态,三级一开始是辅助供能状态。
注意到三级的供能站想要转为供能状态,则需要二级处于不工作状 态;一级供能站想要转为供能状态,则需要二级处于辅助供能状态。
考虑将第二层拆成 供能转不工作 不工作转辅助
然后将有需求的向 供能转不工作 连边
一级功能向两者同时连,均为无穷,最小割割不掉即可。
复杂度正确性存疑。
[×] C
思考到:
你第一次改成了什么,最后就是什么
两个方向的答案是一样的
那么考虑如何快速算答案,双指针预处理出
然后枚举第一步在哪,然后倍增即可。
代码暂无
Codeforces Round 958 (Div. 2)
[√]A
显然每次都扩展
[√]B
考虑可以先把所有的长度大于
接着检查是否
[√]C
考虑到要求相邻
又要求严格递增,那么考虑依次从低位去掉
[√] D
又是一场想完没写完的做法。
场上一直写的是线性做法,我说怎么大家过的都这么快。
首先题意要求每次删除若干不连通点,最后求每个回合中的点权和。
考虑我们在第
那么我们将题目转化为我们需要为每个点赋上一个权
我们容易写出
容易写出转移式即
我们知道
但是我们有完全不需要基于上界的做法。
我们思考
我们设
同时设
即
那么
于是我们发现实际上
那么我们发现我们实际上并不需要维护所有点,我们只需要维护一个点的最小值和次小值位置即可。
那么我们接着来考虑后面这个
在
这里我们采取了直接暴力一点的做法我直接取了
同时我在代码里使用
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 5000005
int T;
ll a[N];
using std::vector;
vector<int>G[N];
int n;
struct P{int ind;ll val;}minn[N],minm[N];
bool operator < (P A,P B){return A.val < B.val;}
bool operator <= (P A,P B){return A.val <= B.val;}
using std::map;
map<int,ll>dval;
vector<int>check;
inline void dfs(int u,int fa){
if(G[u].size() == 0 || (G[u].size() == 1 && G[u][0] == fa)){minn[u].ind = 1;minn[u].val = a[u];minm[u].ind = 2;minm[u].val = 2 * a[u];return ;}
for(auto v : G[u]){
if(v == fa)continue;
dfs(v,u);
}
dval.clear();check.clear();
ll allval = 0;
check.push_back(0);
for(auto v : G[u]){
if(v == fa)continue;
check.push_back(minn[v].ind);
allval += minn[v].val;
dval[minn[v].ind] += minm[v].val - minn[v].val;
}
P nowminn,nowminm;
nowminn.ind = nowminm.ind = 0;
nowminn.val = nowminm.val = 4e18;
for(int i = 1;i < check.size();++i){
ll x = check[i];
P now;now.ind = x;now.val = allval + x * a[u];
if(dval.find(x) != dval.end()){now.val += dval[x];}
if(now.ind != nowminn.ind && now <= nowminn){nowminm = nowminn;nowminn = now;}
else if(now.ind != nowminn.ind && now < nowminm){nowminm = now;}
}
for(int i = 0;i < check.size();++i){
ll x = check[i] + 1;
P now;now.ind = x;now.val = allval + x * a[u];
if(dval.find(x) != dval.end()){now.val += dval[x];}
if(now.ind != nowminn.ind && now <= nowminn ){nowminm = nowminn;nowminn = now;}
else if(now.ind != nowminn.ind && now < nowminm){nowminm = now;}
}
for(int i = 0;i < check.size();++i){
ll x = check[i] + 2;
P now;now.ind = x;now.val = allval + x * a[u];
if(dval.find(x) != dval.end()){now.val += dval[x];}
if(now.ind != nowminn.ind && now <= nowminn ){nowminm = nowminn;nowminn = now;}
else if(now.ind != nowminn.ind && now < nowminm){nowminm = now;}
}
minn[u] = nowminn,minm[u] = nowminm;
}
signed main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%lld",&a[i]);
for(int i = 1;i <= n;++i)G[i].clear();
for(int i = 1;i < n;++i){
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,0);
std::cout<<minn[1].val<<"\n";
}
}
Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)
[√]A
令每个元素为其加一即可。
[√]B
如果前缀有一个地方是
for(int i = 1;i <= n;++i){
if(s[i] == 1)break;
if(s[i] != t[i]){flg = 0;break;}
}
flg ? puts("YES") : puts("NO");
[√]C
对每个点维护从这个点
然后直接双指针,维护到后缀哪里变
[√]D
很有意思的一题,大胆猜测最后一定能连成一颗树
这里给定一个证明,考虑轮数从大到小
令
则在
则依据鸽巢原理定
那么知道这个那直接连边即可。
[√]E
很好的诈骗题,让我头脑旋转
考虑每颗树可以得到
[x]F
回忆欧拉回路的条件,每个点的度数都为偶数。
考虑转成奇偶,那么实际上就是给定了一个初态
实际上就是异或了
我们从线性基的角度来考虑,我们首先找到若干不交线性基
但是需要输出方案就是一个比较困难的事情,由于我们无法和正常线性基一样按位处理。
但是我们发现这若干基里
实际上这个就是题解里所说的从
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 2000005
int T;
int n,m;
struct E{int x,y;E(int x_,int y_):x(x_),y(y_){};};
struct OE{int x,y,id;OE(int x_,int y_,int id_):x(x_),y(y_),id(id_){};};
bool operator < (OE A,OE B){return A.x < B.x;}
using std::vector;
vector<E>e[2];
vector<OE>bas;
int bas_id;
vector<OE>bas_node[N];
int vis_bas[N];
int in[N];
int fa[N];
inline int find(int x){return x == fa[x] ? x : (fa[x] = find(fa[x]));}
vector<int>G[N];
vector<int>S[N];
int nex[N];
int vis[N];
int inbas[N];
using std::stack;
stack<int>ans;
inline void dfs(int u){
for(;nex[u] < G[u].size();++nex[u]){
int v = G[u][nex[u]];
int ind = S[u][nex[u]];
if(vis[ind])continue;
vis[ind] = 1;dfs(v);
}
ans.push(u);
}
int cnt;
inline void print(){
puts("YES");
std::cout<<e[1].size()<<"\n";
for(int i = 1;i <= n;++i)G[i].clear(),S[i].clear(),nex[i] = 0;
cnt = 0;
for(auto em : e[1]){
int x = em.x,y = em.y;
G[x].push_back(y);G[y].push_back(x);
S[x].push_back(++cnt);S[y].push_back(cnt);
}
for(int i = 1;i <= cnt;++i)vis[i] = 0;
dfs(1);
while(ans.size()){std::cout<<ans.top()<<" ";ans.pop();}
puts("");
}
using std::queue;
queue<int>Q;
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d%d",&n,&m);
e[0].clear();e[1].clear();bas.clear();
for(int i = 1;i <= m;++i){
int x,y,op;
scanf("%d%d%d",&x,&y,&op);
e[op].emplace_back(x,y);
}
// puts("YES");
bas_id = 0;
for(int i = 1;i <= n;++i)in[i] = 0,inbas[i] = 0,bas_node[i].clear();
for(int i = 1;i <= n;++i)fa[i] = i;
for(auto em : e[1]){in[em.x] ^= 1 ;in[em.y] ^= 1;}
for(auto em : e[0]){
int x = em.x,y = em.y;
int fx = find(x),fy = find(y);
if(fx == fy){continue;}
fa[fx] = fy;
bas.emplace_back(x,y,++bas_id);
}
// for(auto em : bas){std::cout<<em.x<<" "<<em.y<<"\n";}
for(int i = 1;i <= bas_id;++i)vis_bas[i] = 0;
for(auto em : bas){
inbas[em.x] ++ ;inbas[em.y] ++ ;
bas_node[em.x].push_back(em);bas_node[em.y].push_back(em);
}
while(Q.size())Q.pop();
for(int i = 1;i <= n;++i){if(inbas[i] == 1)Q.push(i);}
while(Q.size()){
int u = Q.front();Q.pop();
for(auto em : bas_node[u]){
int x = u,y = em.x + em.y - u;
if(vis_bas[em.id])continue;
vis_bas[em.id] = 1;
if(in[x]){in[x] ^= 1;in[y] ^= 1;e[1].emplace_back(em.x,em.y);}
inbas[y] -= 1;if(inbas[y] <= 1){Q.push(y);}
}
}
bool flg = 1;
for(int i = 1;i <= n;++i){flg &= (!in[i]);}
if(flg){print();}else puts("NO");
}
}
**本文同步自 24暑期赛训合集
谢谢,你关注的鸽子博主更新了。
上赛季末段没能忍住网瘾, 转生成 ACMer 了
和队友一起拿了块邀请赛金牌和省赛冠军,下半年区域赛不想拖后腿所以还是得努努力啊。
但是因为博主还要跑科研实验 以及 机器人比赛的事情,所以大概一天只能看几个题
唉,被自己菜晕,能不能来点作用,别浪费队友开出来的题。
下列列出的 √ 为自己想出来的,× 为看了题解。
个人训练赛记录,CF / AT 为主
比赛 | sloved problem | rank |
---|---|---|
Codeforces Round #956 (Div. 2) and ByteRace 2024 | 4/7 | 802 |
Codeforces Round 958 (Div. 2) | 3/6 | 1190 |
Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2) | 5/8 | 806 |
Codeforces Round 960 (Div. 2) | 4/7 | 368 |
wanna_be_free 训练赛记录,排名以 vp 榜为主
比赛 | sloved problem | rank |
---|---|---|
2024 (ICPC) Jiangxi Provincial Contest | 10/12 | 10 |
The 2024 CCPC National Invitational Contest (Northeast), The 18th Northeast Collegiate Programming Contest | 12/13 | 5 |
2024 ICPC National Invitational Collegiate Programming Contest, Wuhan Site sloved problem(这场暂时先不写记录了吧,来不及写完了,回来补这场的时候再写) | 6/13 | 106 |
24牛客多校第一场 | 7/11 | 14 |
24牛客多校第二场 | 7/10 | 40 |
24牛客多校第三场 | 6/12 | 70 |
24牛客多校第四场 | 9/12 | 35 |
24牛客多校第五场 | 5/13 | 49 |
24牛客多校第六场 | 7/11 | 32 |
24牛客多校第七场 | 5/12 | 27 |
24牛客多校第八场 | 5/11 | 82 |
[√] 不知名题
求
看这个
思考
所以直接就倍增即可。
//明剑照霜,秋风走马
#include<bits/stdc++.h>
#define ll long long
#define M 600005
ll mod,a,b,c;
int T;
int A[M * 2][80];
ll mul[80];
signed main(){
// freopen("5.in","r",stdin);
// freopen("5.out","w",stdout);
scanf("%lld%lld%lld%lld",&mod,&a,&b,&c);
a = a % mod;
b = b % mod;
c = c % mod;
mul[0] = 1;
for(int i = 1;i <= 60;++i)mul[i] = mul[i - 1] * 2;
for(int i = 0;i <= 2 * mod;++i){A[i][0] = (1ll * a * (i % mod) * (i % mod) % mod+ 1ll * b * (i % mod) % mod + c) % mod + i / 2;}
for(int t = 1;t <= 60;++t){
for(int i = 0;i <= 2 * mod;++i){
A[i][t] = A[A[i][t - 1]][t - 1];
}
}
scanf("%lld",&T);
while(T -- ){
ll k,y;
scanf("%lld%lld",&k,&y);
while((k > 2 * mod) && y){--y;k = (1ll * a * (k % mod) * (k % mod) % mod + 1ll * b * (k % mod) % mod + c) % mod + k / 2;}
if(y == 0){std::cout<<k<<"\n";continue;}
ll now = k;
for(int t = 60;t >= 0;--t){if(y < 0)return 0;if(y >= mul[t]){now = A[now][t];y -= mul[t];}if(!y)break;}
std::cout<<now<<"\n";
}
}
另外 ll 求余 int 居然是 UB 吗,,,,
[√] [Usaco2008 Jan]猜数游戏
给若干区间
问最多前几个区间能够在数组每个都不一样的条件下自洽
首先第一眼看出来了能不能判全局合法,考虑每个相同权值的区间求交,由于每个数不一样则这个最小值一定出现在交集内,若没有交集则错
然后考虑从大权值加到小权值,若一个小权值的区间,在之前已经被大权值区间全部覆盖,则这个区间无法填数 (因为大权值限制条件显然更强)
然后想错了,上来写了个吉司机,一个区间一个区间加入,获得了 6.0 / 100.0 的好成绩
然后既然能判全局合法的方法,那我们直接二分 M 即可。
//山桃红花满上头,蜀江春水拍山流。
#include<bits/stdc++.h>
#define N 2000005
int t[N << 2];
int sum[N << 2];
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define tag(x) t[x]
#define s(x) sum[x]
#define mid ((l + r) >> 1)
int n,q;
#define root 1,1,n
inline void push(int u,int l,int r){if(tag(u) == -1)return ;tag(ls(u)) = tag(rs(u)) = tag(u);s(u) = (r - l + 1) * tag(u);s(ls(u)) = (mid - l + 1) * tag(u);s(rs(u)) = (r - mid) * tag(u);tag(u) = -1;}
inline void up(int u){s(u) = s(ls(u)) + s(rs(u));}
inline void clear(int u,int l,int r){
tag(u) = -1;s(u) = 0;
clear(ls(u),l,mid);clear(rs(u),mid + 1,r);
}
inline void cover(int u,int l,int r,int lt,int rt,int p){
// std::cout<<"COVER "<<u<<" "<<l<<" "<<r<<" "<<lt<<" "<<rt<<" "<<p<<"\n";
if(lt <= l && r <= rt){tag(u) = p;s(u) = (r - l + 1) * tag(u);return ;}
push(u,l,r);
if(lt <= mid)cover(ls(u),l,mid,lt,rt,p);
if(rt > mid)cover(rs(u),mid + 1,r,lt,rt,p);
up(u);
}
inline int query(int u,int l,int r,int lt,int rt){
int ans = 0;
if(lt <= l && r <= rt){return s(u);}
push(u,l,r);
if(lt <= mid)ans += query(ls(u),l,mid,lt,rt);
if(rt > mid)ans += query(rs(u),mid + 1,r,lt,rt);
return ans;
}
struct P{int l,r,v;}A[N],p[N];
using std::map;
map<int,int>L,R;
bool operator < (P A,P B){return A.v > B.v;}
inline bool check(int lim){
for(int i = 1;i <= lim;++i)p[i] = A[i];
cover(root,1,n,0);
L.clear();R.clear();
std::sort(p + 1,p + lim + 1);
// std::cout<<lim<<"\n";
// for(int i = 1;i <= lim;++i)std::cout<<p[i].l<<" "<<p[i].r<<" "<<p[i].v<<"\n";
int las = 1;
for(int i = 1;i <= lim;++i){
//judge and
if(p[i].v != p[i - 1].v){
while(p[las].v != p[i].v){
cover(root,p[las].l,p[las].r,1);
++ las;
}
}
if(L[p[i].v] == 0){L[p[i].v] = p[i].l;R[p[i].v] = p[i].r;}
else {L[p[i].v] = std::max(L[p[i].v],p[i].l);R[p[i].v] = std::min(R[p[i].v],p[i].r);}
if(R[p[i].v] < L[p[i].v]){return 0;}
//judge cover
// std::cout<<"QUERY "<<L[p[i].v]<<" "<<R[p[i].v]<<"\n"<<query(root,L[p[i].v],R[p[i].v])<<"\n";
if(query(root,L[p[i].v],R[p[i].v]) == (R[p[i].v] - L[p[i].v] + 1))return 0;
}
return 1;
}
int main(){
scanf("%d%d",&n,&q);
for(int i = 1;i <= q;++i){scanf("%d%d%d",&A[i].l,&A[i].r,&A[i].v);}
int l = 1,r = q;
while(l + 1 < r){
// std::cout<<l<<" "<<r<<" "<<check(mid)<<"\n";
if(check(mid)){l = mid;}
else r = mid - 1;
}
int ans;
if(check(r))ans = r;
else if(check(l))ans = l;
if(check(q))ans = q;
if(ans == q)puts("0");else std::cout<<ans + 1<<"\n";
}
/*
20 4
1 10 7
5 19 7
3 12 8
11 15 12
*/
[x] k-Maximum Subsequence Sum
每次单点改
询问一个区间上
赛场上完全没懂,下来一看原来以前做过这个题,果然以前训练也太抽象了。
考虑使用反悔贪心的操作,每次选取一个区间最大子段和,然后把这一段取反,再取最大子段和,重复
然后代码不是很好写,但也还行,就是比较麻烦,中间被没预设取反标志值坑了很久.
//山桃红花满上头,蜀江春水拍山流。
#include<bits/stdc++.h>
#define N 100005
struct seg{
int l,r,s;
seg(int l = 0,int r = 0,int s = 0):l(l),r(r),s(s){}
};
seg operator + (seg A,seg B){return seg(A.l,B.r,A.s + B.s);}
bool operator < (seg A,seg B){return (A.s < B.s);}
struct node{
seg lmi,lmx;
seg rmi,rmx;
seg mi,mx;
seg all;
bool rev;
}T[N << 2];
#define lmi(x) x.lmi
#define lmx(x) x.lmx
#define rmi(x) x.rmi
#define rmx(x) x.rmx
#define mx(x) x.mx
#define mi(x) x.mi
#define all(x) x.all
#define tag(x) x.rev
#define L(x) x.l
#define R(x) x.r
#define S(x) x.s
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
inline void init(node &x,int t,int p){
L(lmi(x)) = L(lmx(x)) = L(rmi(x)) = L(rmx(x)) = L(all(x)) = L(mi(x)) = L(mx(x)) = t;
R(lmi(x)) = R(lmx(x)) = R(rmi(x)) = R(rmx(x)) = R(all(x)) = R(mi(x)) = R(mx(x)) = t;
S(lmi(x)) = S(lmx(x)) = S(rmi(x)) = S(rmx(x)) = S(all(x)) = S(mi(x)) = S(mx(x)) = p;
tag(x) = 0;
}
node operator + (node A,node B){
node x;
lmi(x) = std::min(lmi(A),all(A) + lmi(B));
lmx(x) = std::max(lmx(A),all(A) + lmx(B));
rmi(x) = std::min(rmi(B),rmi(A) + all(B));
rmx(x) = std::max(rmx(B),rmx(A) + all(B));
mi(x) = std::min(rmi(A) + lmi(B),std::min(mi(A),mi(B)));
mx(x) = std::max(rmx(A) + lmx(B),std::max(mx(A),mx(B)));
all(x) = all(A) + all(B);
tag(x) = 0;
return x;
}
inline void prind(seg U){printf("[%d,%d],sum: %d\n",L(U),R(U),S(U));}
inline void print(node U,int l,int r){
puts("----------------------------");
printf("THE BLOCK OVER WITH [%d,%d] : \n",l,r);
printf("THE LMI:");prind(lmi(U));
printf("THE LMX:");prind(lmx(U));
printf("THE RMI:");prind(rmi(U));
printf("THE RMX:");prind(rmx(U));
printf("THE MI:");prind(mi(U));
printf("THE MX:");prind(mx(U));
printf("THE ALL:");prind(all(U));
puts("----------------------------");
}
inline void flip(node &x){
tag(x) ^= 1;
std::swap(lmi(x),lmx(x));std::swap(rmi(x),rmx(x));std::swap(mi(x),mx(x));
S(lmi(x)) *= -1;S(lmx(x)) *= -1;S(rmi(x)) *= -1;S(rmx(x)) *= -1;
S(mi(x)) *= -1;S(mx(x)) *= -1;
S(all(x)) *= -1;
}
inline void push(int u){
if(tag(T[u])){
flip(T[ls(u)]);
flip(T[rs(u)]);
tag(T[u]) = 0;
}
}
int n;
int a[N];
#define root 1,1,n
#define mid ((l + r) >> 1)
inline void build(int u,int l,int r){
if(l == r){init(T[u],l,a[l]);return ;}
build(ls(u),l,mid);build(rs(u),mid + 1,r);
T[u] = T[ls(u)] + T[rs(u)];
tag(T[u]) = 0;
}
inline void change(int u,int l,int r,int t,int p){
if(l == r){init(T[u],t,p);return ;}
push(u);
if(t <= mid)change(ls(u),l,mid,t,p);
if(t > mid)change(rs(u),mid + 1,r,t,p);
T[u] = T[ls(u)] + T[rs(u)];
}
inline void cover(int u,int l,int r,int tl,int tr){
if(tl <= l && r <= tr){
flip(T[u]);
return ;
}
push(u);
if(tl <= mid)cover(ls(u),l,mid,tl,tr);
if(tr > mid)cover(rs(u),mid + 1,r,tl,tr);
T[u] = T[ls(u)] + T[rs(u)];
}
inline node query(int u,int l,int r,int tl,int tr){
node ansA,ansB;
L(lmi(ansA)) = L(lmi(ansB)) = -1;
if(tl <= l && r <= tr)return T[u];
if(tl <= mid)ansA = query(ls(u),l,mid,tl,tr);
if(tr > mid)ansB = query(rs(u),mid + 1,r,tl,tr);
if(L(lmi(ansA)) == -1)return ansB;
if(L(lmi(ansB)) == -1)return ansA;
return ansA + ansB;
}
int q;
using std::queue;
std::queue<seg>U;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
build(root);
scanf("%d",&q);
while(q --){
int op;
scanf("%d",&op);
if(op == 0){int t,x;scanf("%d%d",&t,&x);change(root,t,x);}
if(op == 1){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
int ans = 0;
for(int i = 1;i <= k;++i){
node segment = query(root,l,r);
if(S(mx(segment)) > 0){
ans += S(mx(segment));
cover(root,L(mx(segment)),R(mx(segment)));
U.push(mx(segment));
}
}
std::cout<<ans<<"\n";
while(U.size()){
seg top = U.front();
U.pop();
cover(root,L(top),R(top));
}
}
}
}
2024 (ICPC) Jiangxi Provincial Contest
24-7-5 12:00 - 17:00
下面指的是我和队友一起做的情况
[√]A
a + b + c
[√]C
发现如果加起来刚好等于
[√]D
gcd 是质因数次数取 min
lcm 是质因数次数取 max
死去的离散数学整除格正在攻击我
考虑实际上是把每一维质因数的次数排序。
因为
质因数分解然后硬来就行了。
[√]G
队友写的,这里看看题解做法。
考虑每一位的取模贡献
又因为
所以就是加起来取模
[√]H
什么机器学习题
考虑卷积核上每一位的贡献在原矩阵上就是一个矩阵和,使用二维前缀和算出每一位贡献即可。
[√]J
麻将模拟题。
原本以为要判很多牌型,结果只有国士无双十三面和七对子。
队友看懂题之后就直接过了。
[√]K
考虑实际上等价于
[√]L
考虑
[√]F
队友上来就toptree直接写了。
非常的牛。
我只会那个线段树分治加并查集的 2log 做法,但是鉴于我队友看起来是数据结构机器人,感觉很难有他不会的数据结构题,我就不写了。
[√]I
考虑到覆盖边其实是无所谓的条件。
转化成覆盖点。
然后考虑枚举所有的外接圆,一个圆可以由
然后考虑一个圆内能覆盖多少个点,然后统计答案即可。
[x]E
给定一个序列,求找出两个不同的子序列,要求两个子序列和相同,给出方案。
非常苦手,队友上来说应该是什么随机题,非常神秘。
首先知道一个长度
其值域
那在
这说明如果一个序列长度大于等于
那么我们实际上只需要处理
接下来我们考虑如何处理
我们可以使用 mid in mid 的做法,我们处理前十五个的情况和后十五个的情况
容易想到的是我们直接就地使用一个 map,然后每个 map 的下标为 子序列A的和 与 子序列B的和 的差值,然后存的是对应一个状压的三进制数,其中每一位0/1/2表示这个元素没动/在A/在B。
那么我们在 mid in mid 最后统计结果,就是前十五个的 A - B 的差值是后十五个某方案的 A - B 差值的相反数,然后统计方案即可。
但是这样的复杂度大抵是
有点通过不了。
这里其实有很多种处理方法,后面会提到,先介绍题目所说的三个序列归并的方法。
我们考虑我们在搜索状压的时候,做的其实是二元状态
我们从前
实际上是
分别对应上述三种加入方案
那么我们只要在加入到第
你可以直接把三个序列先分别求出来再排序,也可以直接维护三个头指针,三指针移动的把三种对应的最小的先放进
然后我们得到了前十五个的状态,和后十五的状态,由于均有序,我们直接就双指针查有没有相反数即可。
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 1000000
#define M 43046721
int T;
int a[N];
int n;
struct P{
int s_val;int dval;
P(int sv = 0,int dv = 0):s_val(),dval(){};
};
bool operator < (P A,P B){return A.dval < B.dval;}
bool operator == (P A,P B){return A.s_val == B.s_val && A.dval == B.dval;}
void init(P &A,int sv,int dv){A.s_val = sv;A.dval = dv;}
using std::vector;
vector<P>S[2];
/*
0 contain A - B
1 contain B - A
*/
#define inf 5000000000
inline void print(int len,int ind){
for(auto [s_val,dval] : S[ind]){
for(int i = 0;i < len;++i){std::cout<<s_val % 3<<" ";s_val /= 3;}
std::cout<<"val :"<<dval<<"\n";
}
}
inline void del(int l,int r,int ind){//del with [l,r]
int len = r - l + 1;
S[ind].clear();
S[ind].emplace_back(0,0);
for(int i = l;i <= r;++i){
int tnull = 0,tA = 0,tB = 0;
vector<P>ans;
while(tnull < S[ind].size() || tA < S[ind].size() || tB < S[ind].size()){
P cnull,cA,cB;
if(tnull < S[ind].size())init(cnull,S[ind][tnull].s_val * 3,S[ind][tnull].dval);else init(cnull,0,inf);
if(tA < S[ind].size())init(cA,S[ind][tA].s_val * 3 + 1,S[ind][tA].dval + (ind == 0 ? 1 : -1) * a[i]);else init(cA,0,inf);
if(tB < S[ind].size())init(cB,S[ind][tB].s_val * 3 + 2,S[ind][tB].dval + (ind == 0 ? -1 : 1) * a[i]);else init(cB,0,inf);
P minn = std::min(cnull,std::min(cA,cB));
if(cnull == minn){ans.push_back(cnull);tnull ++ ;}
if(cA == minn){ans.push_back(cA);tA ++ ;}
if(cB == minn){ans.push_back(cB);tB ++ ;}
}
S[ind] = ans;
}
// print(len,ind);
}
vector<int>ans[3];
inline void find(int l,int r,int St){
for(int i = r;i >= l;--i){
ans[St % 3].push_back(i);
St /= 3;
}
}
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int i = 1;i <= n;++i){scanf("%d",&a[i]);}
if(n == 1){puts("-1");continue;}
// puts("Yes");
del(1,std::min(15,n / 2),0);del(std::min(16,n / 2 + 1),std::min(30,n),1);
int tpre = 0,tend = 0;
bool flg = 0;
ans[0].clear();ans[1].clear(),ans[2].clear();
while(tpre < S[0].size() && tend < S[1].size()){
if(S[0][tpre].dval == S[1][tend].dval && S[0][tpre].s_val != 0){
flg = 1;
find(1,std::min(15,n / 2),S[0][tpre].s_val);
find(std::min(16,n / 2 + 1),std::min(30,n),S[1][tend].s_val);
break;
}
if(S[0][tpre] == std::min(S[0][tpre],S[1][tend]))tpre ++ ;else tend++;
}
if(!flg)puts("-1");
if(flg){
std::cout<<ans[1].size()<<" ";
for(auto v : ans[1])std::cout<<v<<" ";
puts("");
std::cout<<ans[2].size()<<" ";
for(auto v : ans[2])std::cout<<v<<" ";
puts("");
}
}
}
[√]忘情
求给定一序列,将其分为
容易推导到代价实际为
如果没有
即存在一条斜率为
由于要
接着考虑如何强制选
这是一个经典的条件,我们考虑
我们考虑二分凸包斜率,找到
然后这个斜率我们以
然后我们返回可以分的段数
这里需要注意可能同一斜率上有多个点,这里应该返回最小的段数/最大的段数,后面二分自洽即可,最后的答案减去
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 100005
int n,m;
int a[N];
ll pre[N];
int minn = 2000;
ll f[N];
int t[N];
struct P{
ll x;ll y;int time;
P(ll x_val = 0,ll y_val = 0,int t = 0):x(x_val),y(y_val),time(t){};
};
P s[N];
int top,end;
inline void print(P A){std::cout<<"( "<<A.x<<" "<<A.y<<" "<<A.time<<")"<<"\n";}
inline int check(ll d){
top = end = 1;
s[end] = P(-1,1 + d);
for(int i = 1;i <= n;++i)f[i] = 0;
for(int i = 1;i <= n;++i){
while((end > top) && (2 * pre[i] * (s[top + 1].x - s[top].x) > (s[top + 1].y - s[top].y)))++top;
f[i] = s[top].y - 2 * pre[i] * s[top].x + pre[i] * pre[i];
t[i] = s[top].time + 1;
P now(pre[i] - 1,(pre[i] - 1) * (pre[i] - 1) + d + f[i],t[i]);
while(end > top && (now.y - s[end].y) * (s[end].x - s[end - 1].x) < (s[end].y - s[end - 1].y) * (now.x - s[end].x))--end;
s[++end] = now;
}
return t[n];
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i){scanf("%d",&a[i]);pre[i] = pre[i - 1] + a[i];minn = std::min(a[i],minn);}
ll l = 0,r = 1e18;
#define mid ((l + r) >> 1)
while(l + 1 < r){if(check(mid) > m)l = mid;else r = mid;}
ll ansd = 0;
if(check(l) <= m)ansd = l;
else if(check(r) <= m)ansd = r;
int used = check(ansd);
std::cout<<f[n] - 1ll * m * ansd<<"\n";
}
[√]CF1499F Diameter Cuts
给定一颗
令
特殊的
考虑如何统计答案,实际上就是任两子树内最长路径相加不大于
考虑如果显然子树内不能存在两条大于
若不存在大于
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 5005
#define mod 998244353
int f[N][N];// n longest n
int pre[N][N];// sum of pre
int fa[N];
int ps[N];
using std::vector;
vector<int>G[N];
int n,k;
inline int qpow(int x,int t){
int a = x;
int ans = 1;
while(t){
if(t & 1)ans = 1ll * ans * a % mod;
a = (1ll * a * a) % mod;
t >>= 1;
}
return ans;
}
inline int inv(int x){return qpow(x,mod - 2);}
inline void dfs(int u = 1,int ffa = 0){
fa[u] = ffa;
for(auto v : G[u]){
if(v == fa[u])continue;
dfs(v,u);
}
}
inline void del(int u = 1){
for(int i = 0;i <= k;++i)ps[i] = 1;
for(auto v : G[u]){
if(v == fa[u])continue;
del(v);
}
for(int i = 0;i <= k;++i)ps[i] = 1;
for(auto v : G[u]){
if(v == fa[u])continue;
for(int i = 0;i <= k;++i)ps[i] = 1ll * ps[i] * pre[v][i] % mod;
}
/*connected*/
for(int i = 0;i <= k / 2;++i)f[u][i + 1] = (ps[i] - (i != 0 ? ps[i - 1] : 0) + mod) % mod;
/*unconneted*/
f[u][0] = 0;
for(auto v : G[u]){
if(v == fa[u])continue;
for(int i = k / 2 + 1;i <= k;++i){
int now = 1ll * f[v][i] * ps[k - i] % mod * inv(pre[v][k - i]) % mod;
f[u][0] = (1ll * f[u][0] + now) % mod;
f[u][i + 1] = (1ll * f[u][i + 1] + now) % mod;
}
}
f[u][0] = (f[u][0] + ps[k / 2]) % mod;
pre[u][0] = f[u][0];
for(int i = 1;i <= k + 1;++i)pre[u][i] = (pre[u][i - 1] + f[u][i]) % mod;
}
int main(){
scanf("%d%d",&n,&k);
for(int i = 1;i < n;++i){
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs();
del();
std::cout<<f[1][0]<<"\n";
}
Codeforces Round #956 (Div. 2) and ByteRace 2024
[√]A
构造一个
直接输出
[√]B
给定两矩阵
考虑到实际上每行每列的对
充分条件可以考虑每行调整即可。
[√]C
给定
考虑枚举中间那段在哪,然后双指针即可,考虑显然是前一段为
判断是否满足条件即可
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
int n;
int a[N][3];
ll prea[N][3];
ll enda[N][3];
ll tot;
int T;
int ans[2][3];
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int j = 0;j <= 2;++j)
for(int i = 1;i <= n;++i)scanf("%d",&a[i][j]);
for(int j = 0;j <= 2;++j)
for(int i = 0;i <= n + 1;++i)prea[i][j] = enda[i][j] = 0;
for(int j = 0;j <= 2;++j)
for(int i = 1;i <= n;++i)prea[i][j] = prea[i - 1][j] + a[i][j];
for(int j = 0;j <= 2;++j)
for(int i = n;i >= 1;--i)enda[i][j] = enda[i + 1][j] + a[i][j];
bool flg = 0;
ll target = std::ceil(1.0 * prea[n][0] / 3);
// std::cout<<target<<"\n";
for(int j = 0;j <= 2;++j){
// std::cout<<"check "<<j<<"\n";
int t1 = (j + 1) % 3,t2 = (j + 2) % 3;
int ta = 1;
for(int i = 2;i <= n;++i){
while(prea[i][j] - prea[ta][j] >= target)ta ++ ;
// std::cout<<ta<<" "<<i<<"\n";
// std::cout<<t1<<" "<<prea[ta - 1][t1]<<" "<<t2<<" "<<enda[i + 1][t2]<<"\n";
if(prea[ta - 1][t1] >= target && enda[i + 1][t2] >= target){
ans[0][t1] = 1,ans[1][t1] = ta - 1;
ans[0][j] = ta,ans[1][j] = i;
ans[0][t2] = i + 1,ans[1][t2] = n;
flg = 1;
break;
}
std::swap(t1,t2);
// std::cout<<t1<<" "<<prea[ta - 1][t1]<<" "<<t2<<" "<<enda[i + 1][t2]<<"\n";
if(prea[ta - 1][t1] >= target && enda[i + 1][t2] >= target){
ans[0][t1] = 1,ans[1][t1] = ta - 1;
ans[0][j] = ta,ans[1][j] = i;
ans[0][t2] = i + 1,ans[1][t2] = n;
flg = 1;
break;
}
}
if(flg)break;
}
if(flg){
for(int i = 0;i <= 2;++i)
for(int j = 0;j <= 1;++j)std::cout<<ans[j][i]<<" ";
puts("");
}else puts("-1");
}
}
[√]D
给定两个序列
问能否最后相同。
显然权值集合要相同
然后跨越
所以充要条件是
考虑充分条件构造,直接把一个调整到
[√]F
给定一个序列
问区间代价第
先二分
然后考虑如何计算
我们考虑枚举子点对的右端点
考虑如何统计所有的数量,你显然可以
我们考虑可以扫描线,我们双指针维护前缀最大的
只有
使用 01-tire 来进行查询过程,两支
也有一个
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 100005
#define inf 2000000000
#define int ll
using std::multiset;
int T;
int n,k;
int a[N];
int A[N * 50][2];
int siz[N * 50];
int cnt = 1;
inline void insert(int x){
int rt = 1;
for(int i = 31;i >= 0;--i){
siz[rt] ++ ;
int t = ((x >> i) & 1);
if(!A[rt][t])A[rt][t] = ++cnt;
rt = A[rt][t];
}
siz[rt] ++ ;
}
inline void erase(int x){
int rt = 1;
if(x == -1)return ;
for(int i = 31;i >= 0;--i){
siz[rt] -- ;
int t = ((x >> i) & 1);
if(!A[rt][t])A[rt][t] = ++cnt;
rt = A[rt][t];
}
siz[rt] -- ;
}
inline int find(int x){
int rt = 1;
int ans = 0;
if(siz[rt] == 0)return inf;
for(int i = 31;i >= 0;--i){
int t = ((x >> i) & 1);
if(siz[A[rt][t]])rt = A[rt][t];
else rt = A[rt][t ^ 1],ans |= (1ll << i);
}
return ans;
}
int lasl[N];
inline int check(int x){
int l = 0;
int ans = 0;
a[0] = -1;
for(int i = 1;i <= n;++i){
bool flg = (find(a[i]) <= x);
if(flg){
while(find(a[i]) <= x){erase(a[l ++ ]);}
insert(a[-- l]);
ans = ans + (l - lasl[i - 1]) * (n - i + 1);
}
lasl[i] = l;
insert(a[i]);
}
while(l <= n){erase(a[l ++ ]);}
return ans;
}
inline void slove(){
int l = 0,r = inf;
#define mid ((l + r) >> 1)
while(l + 1 < r){
if(check(mid) >= k)r = mid;
else l = mid + 1;
}
if(check(l) >= k)std::cout<<l<<"\n";
else std::cout<<r<<"\n";
}
signed main(){
scanf("%lld",&T);
while(T -- ){
scanf("%lld%lld",&n,&k);
for(int i = 1;i <= n;++i)scanf("%lld",&a[i]);
slove();
}
}
[x]E
我们只先计算先手球的期望。
首先思考到所有的特殊球和普通球的贡献是分开的
然后普通球的贡献是好计算的,
然后思考特殊球的贡献,我当时以为是分成两段,但是后来看来是用
The 2024 CCPC National Invitational Contest (Northeast), The 18th Northeast Collegiate Programming Contest
[√]A
考虑根号操作只有
那么考虑先对
[√]D
队友发现答案一直是lose,输出
[√]E
要求
考虑反过来
[√]M
考虑如何不重复的计这个,考虑枚举中间按两个点,然后尝试枚举下面矩形点对,然后正常统计即可。队友写了各种叉积判断避免了精度问题。
[√]H
给定
考虑二分答案,对于一个点对的方案形为
考虑其为
[√]I
考虑最后一个完整排列的
其中
这个考虑容斥即可
[√]K
考虑一层层的从右到左的放置
下一层查询在上一层的左端点和其离的最近的
[√]L
考虑先取所有最小单位
[√]F
若
对
考虑直接
[√]G
考虑对出现次数根号分治即可
含有出现次数大的那边离线下来做,小的直接暴力。
[√] B
实际上每一层都实际上依赖于第二层
假设二级一开始是工作状态,则一级一开始是不工作状态,三级一开始是辅助供能状态。
注意到三级的供能站想要转为供能状态,则需要二级处于不工作状 态;一级供能站想要转为供能状态,则需要二级处于辅助供能状态。
考虑将第二层拆成 供能转不工作 不工作转辅助
然后将有需求的向 供能转不工作 连边
一级功能向两者同时连,均为无穷,最小割割不掉即可。
复杂度正确性存疑。
[×] C
思考到:
你第一次改成了什么,最后就是什么
两个方向的答案是一样的
那么考虑如何快速算答案,双指针预处理出
然后枚举第一步在哪,然后倍增即可。
代码暂无
Codeforces Round 958 (Div. 2)
[√]A
显然每次都扩展
[√]B
考虑可以先把所有的长度大于
接着检查是否
[√]C
考虑到要求相邻
又要求严格递增,那么考虑依次从低位去掉
[√] D
又是一场想完没写完的做法。
场上一直写的是线性做法,我说怎么大家过的都这么快。
首先题意要求每次删除若干不连通点,最后求每个回合中的点权和。
考虑我们在第
那么我们将题目转化为我们需要为每个点赋上一个权
我们容易写出
容易写出转移式即
我们知道
但是我们有完全不需要基于上界的做法。
我们思考
我们设
同时设
即
那么
于是我们发现实际上
那么我们发现我们实际上并不需要维护所有点,我们只需要维护一个点的最小值和次小值位置即可。
那么我们接着来考虑后面这个
在
这里我们采取了直接暴力一点的做法我直接取了
同时我在代码里使用
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 5000005
int T;
ll a[N];
using std::vector;
vector<int>G[N];
int n;
struct P{int ind;ll val;}minn[N],minm[N];
bool operator < (P A,P B){return A.val < B.val;}
bool operator <= (P A,P B){return A.val <= B.val;}
using std::map;
map<int,ll>dval;
vector<int>check;
inline void dfs(int u,int fa){
if(G[u].size() == 0 || (G[u].size() == 1 && G[u][0] == fa)){minn[u].ind = 1;minn[u].val = a[u];minm[u].ind = 2;minm[u].val = 2 * a[u];return ;}
for(auto v : G[u]){
if(v == fa)continue;
dfs(v,u);
}
dval.clear();check.clear();
ll allval = 0;
check.push_back(0);
for(auto v : G[u]){
if(v == fa)continue;
check.push_back(minn[v].ind);
allval += minn[v].val;
dval[minn[v].ind] += minm[v].val - minn[v].val;
}
P nowminn,nowminm;
nowminn.ind = nowminm.ind = 0;
nowminn.val = nowminm.val = 4e18;
for(int i = 1;i < check.size();++i){
ll x = check[i];
P now;now.ind = x;now.val = allval + x * a[u];
if(dval.find(x) != dval.end()){now.val += dval[x];}
if(now.ind != nowminn.ind && now <= nowminn){nowminm = nowminn;nowminn = now;}
else if(now.ind != nowminn.ind && now < nowminm){nowminm = now;}
}
for(int i = 0;i < check.size();++i){
ll x = check[i] + 1;
P now;now.ind = x;now.val = allval + x * a[u];
if(dval.find(x) != dval.end()){now.val += dval[x];}
if(now.ind != nowminn.ind && now <= nowminn ){nowminm = nowminn;nowminn = now;}
else if(now.ind != nowminn.ind && now < nowminm){nowminm = now;}
}
for(int i = 0;i < check.size();++i){
ll x = check[i] + 2;
P now;now.ind = x;now.val = allval + x * a[u];
if(dval.find(x) != dval.end()){now.val += dval[x];}
if(now.ind != nowminn.ind && now <= nowminn ){nowminm = nowminn;nowminn = now;}
else if(now.ind != nowminn.ind && now < nowminm){nowminm = now;}
}
minn[u] = nowminn,minm[u] = nowminm;
}
signed main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%lld",&a[i]);
for(int i = 1;i <= n;++i)G[i].clear();
for(int i = 1;i < n;++i){
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,0);
std::cout<<minn[1].val<<"\n";
}
}
Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)
[√]A
令每个元素为其加一即可。
[√]B
如果前缀有一个地方是
for(int i = 1;i <= n;++i){
if(s[i] == 1)break;
if(s[i] != t[i]){flg = 0;break;}
}
flg ? puts("YES") : puts("NO");
[√]C
对每个点维护从这个点
然后直接双指针,维护到后缀哪里变
[√]D
很有意思的一题,大胆猜测最后一定能连成一颗树
这里给定一个证明,考虑轮数从大到小
令
则在
则依据鸽巢原理定
那么知道这个那直接连边即可。
[√]E
很好的诈骗题,让我头脑旋转
考虑每颗树可以得到
[x]F
回忆欧拉回路的条件,每个点的度数都为偶数。
考虑转成奇偶,那么实际上就是给定了一个初态
实际上就是异或了
我们从线性基的角度来考虑,我们首先找到若干不交线性基
但是需要输出方案就是一个比较困难的事情,由于我们无法和正常线性基一样按位处理。
但是我们发现这若干基里
实际上这个就是题解里所说的从
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
#define N 2000005
int T;
int n,m;
struct E{int x,y;E(int x_,int y_):x(x_),y(y_){};};
struct OE{int x,y,id;OE(int x_,int y_,int id_):x(x_),y(y_),id(id_){};};
bool operator < (OE A,OE B){return A.x < B.x;}
using std::vector;
vector<E>e[2];
vector<OE>bas;
int bas_id;
vector<OE>bas_node[N];
int vis_bas[N];
int in[N];
int fa[N];
inline int find(int x){return x == fa[x] ? x : (fa[x] = find(fa[x]));}
vector<int>G[N];
vector<int>S[N];
int nex[N];
int vis[N];
int inbas[N];
using std::stack;
stack<int>ans;
inline void dfs(int u){
for(;nex[u] < G[u].size();++nex[u]){
int v = G[u][nex[u]];
int ind = S[u][nex[u]];
if(vis[ind])continue;
vis[ind] = 1;dfs(v);
}
ans.push(u);
}
int cnt;
inline void print(){
puts("YES");
std::cout<<e[1].size()<<"\n";
for(int i = 1;i <= n;++i)G[i].clear(),S[i].clear(),nex[i] = 0;
cnt = 0;
for(auto em : e[1]){
int x = em.x,y = em.y;
G[x].push_back(y);G[y].push_back(x);
S[x].push_back(++cnt);S[y].push_back(cnt);
}
for(int i = 1;i <= cnt;++i)vis[i] = 0;
dfs(1);
while(ans.size()){std::cout<<ans.top()<<" ";ans.pop();}
puts("");
}
using std::queue;
queue<int>Q;
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d%d",&n,&m);
e[0].clear();e[1].clear();bas.clear();
for(int i = 1;i <= m;++i){
int x,y,op;
scanf("%d%d%d",&x,&y,&op);
e[op].emplace_back(x,y);
}
// puts("YES");
bas_id = 0;
for(int i = 1;i <= n;++i)in[i] = 0,inbas[i] = 0,bas_node[i].clear();
for(int i = 1;i <= n;++i)fa[i] = i;
for(auto em : e[1]){in[em.x] ^= 1 ;in[em.y] ^= 1;}
for(auto em : e[0]){
int x = em.x,y = em.y;
int fx = find(x),fy = find(y);
if(fx == fy){continue;}
fa[fx] = fy;
bas.emplace_back(x,y,++bas_id);
}
// for(auto em : bas){std::cout<<em.x<<" "<<em.y<<"\n";}
for(int i = 1;i <= bas_id;++i)vis_bas[i] = 0;
for(auto em : bas){
inbas[em.x] ++ ;inbas[em.y] ++ ;
bas_node[em.x].push_back(em);bas_node[em.y].push_back(em);
}
while(Q.size())Q.pop();
for(int i = 1;i <= n;++i){if(inbas[i] == 1)Q.push(i);}
while(Q.size()){
int u = Q.front();Q.pop();
for(auto em : bas_node[u]){
int x = u,y = em.x + em.y - u;
if(vis_bas[em.id])continue;
vis_bas[em.id] = 1;
if(in[x]){in[x] ^= 1;in[y] ^= 1;e[1].emplace_back(em.x,em.y);}
inbas[y] -= 1;if(inbas[y] <= 1){Q.push(y);}
}
}
bool flg = 1;
for(int i = 1;i <= n;++i){flg &= (!in[i]);}
if(flg){print();}else puts("NO");
}
}
[x] G
考虑每一位的进位不会超过
令后续进位大小为
我们可以考虑
则这个地方我们直接暴力设计
所以在设计
//樱花落尽阶前月,象床愁倚薰笼
#include<bits/stdc++.h>
#define ll long long
int T;
int n,k;
using std::vector;
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d%d",&n,&k);
std::string s;
vector<std::string>A(n + 1);
vector<vector<bool>>f(k + 1,vector<bool>(4 * n));
vector<vector<int>>g(k + 1,vector<int>(4 * n));
vector<vector<int>>las(k + 1,vector<int>(4 * n));
std::cin>>s;
f[k][0] = 1;
for(int i = 0;i <= k - 1;++i)
for(int j = 0;j <= 2 * n;++j)f[i][j] = 0;
for(int i = 1;i <= n;++i)std::cin>>A[i];
for(int i = k - 1;i >= 0;--i){
int now = s[i] - '0';
int cnt[2];
cnt[0] = cnt[1] = 0;
for(int j = 1;j <= n;++j){cnt[A[j][i] - '0'] ++ ;}
int t0 = cnt[1],t1 = cnt[0];
// std::cout<<"del "<<i<<"\n";
// std::cout<<now<<" "<<cnt[0]<<" "<<cnt[1]<<"\n";
for(int sum = 0;sum <= 2 * n;++sum){
if(f[i + 1][sum]){
int res0 = (t0 + sum) % 2;
int res1 = (t1 + sum) % 2;
// std::cout<<"LAS "<<sum<<" "<<res0<<" "<<res1<<"\n";
if(res0 == now){f[i][(t0 + sum) / 2] = 1;g[i][(t0 + sum) / 2] = 0;las[i][(t0 + sum) / 2] = sum;}
if(res1 == now){f[i][(t1 + sum) / 2] = 1;g[i][(t1 + sum) / 2] = 1;las[i][(t1 + sum) / 2] = sum;}
}
}
}
if(!f[0][0]){puts("-1");continue;}
int now = 0;
for(int i = 0;i <= k - 1;++i){
std::cout<<g[i][now];
now = las[i][now];
}
puts("");
}
}
Codeforces Round 960 (Div. 2)
[√]A
大胆猜测若有一种为奇数则可以
[√]B
简单构造,可以看代码
Submission #271558205 - Codeforces
[√]C
Submission #271575984 - Codeforces
忘记当时在写什么了。
[√]D
思考每行至多在 [0,4] 位置放置即可
Submission #271592202 - Codeforces
[√]E1
独立做出的第一个构造题。
考虑树根号分块然后撒点,将其确认到一条链上,然后询问根号次将其移动到点上分即可。
//自在飞花轻似梦,无边丝雨细如愁。
#include<bits/stdc++.h>
#define ll long long
#define N 200005
int cnt;
inline int request(int x){cnt ++ ;std::cout<<"? "<<x<<std::endl;scanf("%d",&x);return x;}
int T;
int n;
using std::vector;
vector<int>G[N];
vector<int>H[N];
int line[N];
int fa[N];
inline void dfs(int u,int ffa){
if(G[u].size() == 1 && G[u][0] == ffa){line[u] = 1;return;}
fa[u] = ffa;
for(auto v : G[u]){
if(v == ffa)continue;
dfs(v,u);
line[u] = std::max(line[v] + 1,line[u]);
}
}
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int i = 1;i <= n;++i)G[i].clear(),H[i].clear();
for(int i = 1;i < n;++i){
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);G[y].push_back(x);
}
dfs(1,0);
for(int i = 1;i <= n;++i)H[line[i]].push_back(i);
int bas = 40;
int rt = 0;int nrt = 0;
int leaf = H[1][0];
cnt = 0;
if(request(leaf)){std::cout<<"! "<<leaf<<std::endl;continue;}else{nrt = leaf;}
for(int deep = bas;deep < line[1];deep += bas){
for(auto u : H[deep]){
if(request(u)){rt = u;break;}
nrt = u;
}
if(rt)break;
}
if(!rt)rt = 1;
if(rt == 1){while(cnt < 300)request(nrt);std::cout<<"! 1"<<std::endl;continue;}
while(1){
request(nrt);
if(!request(rt)){std::cout<<"! "<<(fa[fa[rt]] ? fa[fa[rt]] : 1)<<std::endl;break;}
}
}
}
[x] E2
考虑先询问
直接
//自在飞花轻似梦,无边丝雨细如愁。
#include<bits/stdc++.h>
#define ll long long
#define N 200005
#define bas 70
int cnt;
inline int request(int x){
std::cout<<"? "<<x<<std::endl;
int res;
scanf("%d",&res);
return res;
}
int T;
int n;
using std::vector;
vector<int>G[N];
vector<int>H[N];
int line[N];
int fa[N];
int deep[N];
inline void dfs(int u,int ffa){
deep[u] = deep[ffa] + 1;
fa[u] = ffa;
if(G[u].size() == 1 && G[u][0] == ffa){line[u] = 1;return;}
for(auto v : G[u]){
if(v == ffa)continue;
dfs(v,u);
line[u] = std::max(line[v] + 1,line[u]);
}
}
inline int find(int u,int ffa){
// std::cout<<u<<" "<<ffa<<"\n";
vector<int>L;L.clear();
for(auto v : G[u]){
if(v == ffa)continue;
if(line[v] > bas)L.push_back(v);
}
if(L.size() == 0)return u;
int nex = L[0];
for(int i = 1;i < L.size();++i){
int v = L[i];
if(request(v)){nex = v;break;}
}
return find(nex,u);
}
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int i = 1;i <= n;++i)G[i].clear(),H[i].clear();
for(int i = 1;i < n;++i){
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);G[y].push_back(x);
}
dfs(1,0);
for(int i = 1;i <= n;++i)H[line[i]].push_back(i);
int rt = 0;int nrt = 0;
int leaf = H[1][0];
cnt = 0;
// std::cout<<leaf<<"\n";
if(request(leaf)){std::cout<<"! "<<leaf<<std::endl;continue;}else{nrt = leaf;}
for(int i = 1;i <= bas - 1;++i)request(leaf);
int end = find(1,0);
// std::cout<<end<<"\n";
vector<int>L;L.clear();
int u = end;
while(u != 1){L.push_back(u);u = fa[u];}L.push_back(1);std::reverse(L.begin(),L.end());
int l = 1,r = deep[end];
#define mid ((l + r + 1) >> 1)
int ncnt = 0;
int ans = 0;
// for(auto v : L){std::cout<<v<<" ";}
// puts("");
while(l < r){
if(request(L[std::max(mid - 1 - ncnt,0)])){l = mid;}
else ncnt ++ ,r = mid - 1;
}
ans = L[std::max(l - 1 - ncnt,0)];
std::cout<<"! "<<ans<<std::endl;
}
}
本文作者:fhq_treap
本文链接:https://www.cnblogs.com/dixiao/p/18279702
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效