2023 集训记录
2023.10.9 NOIP2023-div2模拟赛15
A. 华灵
显然,一个合法括号对序列的长度必须为奇数,所以当 \(n\) 或 \(m\) 为奇数的时候,直接构造。下考虑 \(n\) 和 \(m\) 都是偶数的情况。设 \(n\leq m\) 的情况。
其他情况,可以构造出两种方案,\(n/2+m-1\) 和 \(n+m-4\),取 \(\texttt{min}\) 即可。具体构造见代码。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
freopen("butterfly.in","r",stdin);
freopen("butterfly.out","w",stdout);
scanf("%d%d",&n,&m);
if(n&1){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j&1)printf("(");
else printf(")");
}
printf("\n");
}
}
else if(m&1){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i&1)printf("(");
else printf(")");
}
printf("\n");
}
}
else if(n>m){
if(n+m/2-1>n+m-4){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i&1){
if(j&1)printf("(");
else printf(")");
}
else{
if(j==1)printf("(");
else if(j==m)printf(")");
else if(j&1)printf(")");
else printf("(");
}
}
printf("\n");
}
}
else{
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1||j==1)printf("(");
else if(i==n||j==m)printf(")");
else if((i+j)&1)printf("(");
else printf(")");
}
printf("\n");
}
}
}
else{
if(m+n/2-1>n+m-4){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j&1){
if(i&1)printf("(");
else printf(")");
}
else{
if(i==1)printf("(");
else if(i==n)printf(")");
else if(i&1)printf(")");
else printf("(");
}
}
printf("\n");
}
}
else{
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1||j==1)printf("(");
else if(i==n||j==m)printf(")");
else if((i+j)&1)printf("(");
else printf(")");
}
printf("\n");
}
}
}
return 0;
}
B. 最近公共祖先
方法一(正解)
考虑加入一个黑点更新其他点。暴力的做法是不停地往上跳,用线段树维护一个 \(\texttt{RMQ}\),更新除这个点跳上去的子树以外的其他点。
考虑到一件事情:如果一个子树里有两个黑点,那么整棵子树都能被这个节点更新,那么后面更新到这个点都不会再有贡献,所以直接 break
掉即可。
这样一个点最多被算 \(2\) 次,总复杂度 \(\mathcal{O}(n\log n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=2e5+10,INF=0x3f3f3f3f;
int n,m,a[N],fa[N];
int head[N],nxt[M],to[M],cnt=1;
int dfn[N],tim=0,ed[N];
bool used[N];
void addEdge(int u,int v){
nxt[++cnt]=head[u];
head[u]=cnt;
to[cnt]=v;
}
void dfs(int u,int p){
fa[u]=p;
dfn[u]=++tim;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==p)continue;
dfs(v,u);
}
ed[u]=tim;
}
struct Node{
int l,r;
int val,tag;
}tree[N<<2];
void pushup(int x){
tree[x].val=max(tree[x<<1].val,tree[x<<1|1].val);
}
void pushdown(int x){
if(tree[x].tag==-INF)return;
tree[x<<1].val=max(tree[x<<1].val,tree[x].tag);
tree[x<<1|1].val=max(tree[x<<1|1].val,tree[x].tag);
tree[x<<1].tag=max(tree[x<<1].tag,tree[x].tag);
tree[x<<1|1].tag=max(tree[x<<1|1].tag,tree[x].tag);
tree[x].tag=-INF;
}
void build(int x,int l,int r){
tree[x].l=l,tree[x].r=r;
tree[x].val=tree[x].tag=-INF;
if(l==r)return;
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
}
void update(int x,int l,int r,int v){
if(tree[x].l>=l&&tree[x].r<=r){
tree[x].val=max(tree[x].val,v);
tree[x].tag=max(tree[x].tag,v);
return;
}
pushdown(x);
int mid=(tree[x].l+tree[x].r)>>1;
if(l<=mid)update(x<<1,l,r,v);
if(r>mid)update(x<<1|1,l,r,v);
pushup(x);
}
int query(int x,int p){
if(tree[x].l==tree[x].r)return tree[x].val;
pushdown(x);
int mid=(tree[x].l+tree[x].r)>>1;
if(p<=mid)return query(x<<1,p);
else return query(x<<1|1,p);
}
int main(){
freopen("lca.in","r",stdin);
freopen("lca.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
addEdge(u,v);
addEdge(v,u);
}
dfs(1,0);
build(1,1,n);
char op[10];
int u;
while(m--){
scanf("%s%d",op,&u);
if(op[0]=='M'){
int v=u,lst=0;
while(v&&!used[v]){
used[v]=1;
if(v==u)update(1,dfn[v],ed[v],a[v]);
else{
if(dfn[v]<dfn[lst])update(1,dfn[v],dfn[lst]-1,a[v]);
if(ed[lst]<ed[v])update(1,ed[lst]+1,ed[v],a[v]);
}
lst=v;
v=fa[v];
}
if(v)update(1,dfn[v],ed[v],a[v]);
}
else{
int res=query(1,dfn[u]);
if(res==-INF)printf("-1\n");
else printf("%d\n",res);
}
}
return 0;
}
方法二(询问分块,会 TLE 80 pts,powered by Sai_tqwq)
Sai_tqwq 提供的一个时间复杂度为 \(\mathcal{O}(n\sqrt{n\log n})\) 的做法。
询问分块,每 \(\sqrt{n}\) 次重构一次,散点暴力,整块的可以 \(\texttt{DFS}\) \(\mathcal{O}(n)\) 处理。
题解有一个这个做法,但是被 Sai_tqwq 卡掉了,讨论。
C. 樱符
妙妙题。主要是观察一个性质。
性质:因为 \(\forall u,v,\texttt{maxflow}(u,v)\leq 2\),则原图是一个仙人掌。
证法是利用最大流等于最小割,然后显然如果不是仙人掌,至少割三条边。
知道仙人掌以后,就会发现任意两点的最大流,即最小割,要么割掉一个桥边,要么割掉环上的两条边。
发现如果要割环上的两条边,必然要割最小的边,那就删掉最小的边,并且把剩下的环上的边权都加上最小的边权。
因为是仙人掌,所以剩下形成一个数,两个点的最大流为树上路径的和。把边从大到小加入,用并查集维护 \(p^{(i-1)n}\) 和 \(p^i\) 即可。
时间复杂度 \(\mathcal{O}(m\log n)\)。
D. 收容
不甚牛。感觉不是很难想(?
首先,将输入质因数分解。然后从大到小枚举 \(b_i\),用一个 set
维护上下两者之商的最小值即可(或者用 \(\texttt{segment tree}\) 也可以)。
时间复杂度 \(\mathcal{O}(V\log V)\)。
2023.10.10 NOIP2023-div2模拟赛16
A. 正方形
搞笑了,Sai_tqwq 造了个极限数据卡掉了几乎所有 \(O(nd)\) 的代码,然后数据把 \(O(nd^2)\) 都放过去了?卡了一整场常数结果第一发就过了??
讲一下 \(O(nd)\) 的做法罢。显然,每个单位正方形都只会被分成 \(4\) 份,维护 \(0\sim 15\) 的状态即可表示每个正方形的状态。
对每一列进行一个差分即可,斜着的正方形需要维护边上斜着的三角形。时间复杂度 \(O(nd)\)。
B. 完全背包问题
没有限制就是同余最短路板子题。考虑到 \(L\) 和 \(C\) 都很小,这样直接背包即可。
2023.10.12 NOIP2023-div2模拟赛17
A. yxh 与数组
简单题。直接暴力处理 \(x!\) 的质因数分解即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
// bool st;
const int N=1e7+10;
bool ispri[N];
int pri[N],cnt=0;
int cur[N];
int q;
// bool en;
void precalc()
{
for(int i=2;i<N;i++)
{
if(!ispri[i])
{
pri[++cnt]=i;
}
for(int j=1;j<=cnt&&i*pri[j]<N;j++)
{
ispri[i*pri[j]]=1;
if(i%pri[j]==0)
{
break;
}
}
}
}
void calc(int x,int v)
{
for(int i=1;i<=cnt&&pri[i]<=x;i++)
{
int num=0,tmp=x;
while(tmp)
{
tmp/=pri[i];
num+=tmp;
}
cur[i]+=(v==1?num:-num);
}
}
void solve()
{
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
for(int i=1;i<=cnt;i++)
{
cur[i]=0;
}
calc(a-1,1);
calc(b,-1);
calc(c-1,-1);
calc(d,1);
for(int i=1;i<=cnt;i++)
{
if(cur[i]<0)
{
printf("NE\n");
return;
}
}
printf("DA\n");
}
int main()
{
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
precalc();
scanf("%d",&q);
while(q--)
{
solve();
}
return 0;
}
B. 谦逊
发现次数不会太多,直接暴搜即可。(P.S. 赛时写的贪心,假掉了。)
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> pii;
int mn = 10, res;
inline int get(int x) {
int sum = 0;
while(x) {
sum += x % 10;
x /= 10;
}
return sum;
}
inline pii calc(int x) {
int cnt = 0;
while(x >= 10) {
cnt++;
x = get(x);
}
return make_pair(x, cnt);
}
int n, k;
void dfs(int x, int cur) {
if(x > 14) return;
if(cur < mn) {
mn = cur;
res = x;
} else if(cur == mn && x < res) res = x;
dfs(x + 1, cur + k);
dfs(x + 1, get(cur));
}
void solve() {
cin >> n >> k;
mn = 10;
dfs(0, n);
cout << mn << " " << res << "\n";
}
signed main() {
freopen("humility.in", "r", stdin);
freopen("humility.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--) solve();
return 0;
}
C. 基础fake 练习题
贪心地选深度最大的链即可,树链剖分更新,赛时被数据卡常了(难过。
点击查看代码
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
inline int min(int x,int y)
{
return x<y?x:y;
}
const int N=3e5+10,M=6e5+10;
int n,m,c[N];
int head[N],nxt[M],to[M],cnt=1;
pii q[N];
int dep[N],siz[N],son[N],top[N],fa[N];
int dfn[N],tim=0,rnk[N];
int val[N<<2],tag[N<<2];
void addEdge(int u,int v)
{
nxt[++cnt]=head[u];
head[u]=cnt;
to[cnt]=v;
}
void dfs(int u,int p)
{
fa[u]=p;
dep[u]=dep[p]+1;
siz[u]=1;
son[u]=-1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==p)
{
continue;
}
dfs(v,u);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]])
{
son[u]=v;
}
}
}
void redfs(int u,int t)
{
dfn[u]=++tim;
rnk[tim]=u;
top[u]=t;
if(~son[u])
{
redfs(son[u],t);
}
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa[u]||v==son[u])
{
continue;
}
redfs(v,v);
}
}
bool cmp(pii x,pii y)
{
return dep[x.first]>dep[y.first];
}
void pushup(int x)
{
val[x]=min(val[x<<1],val[x<<1|1]);
}
void pushdown(int x)
{
if(!tag[x])
{
return;
}
val[x<<1]-=tag[x];
val[x<<1|1]-=tag[x];
tag[x<<1]+=tag[x];
tag[x<<1|1]+=tag[x];
tag[x]=0;
}
void build(int x,int l,int r)
{
tag[x]=0;
if(l==r)
{
val[x]=c[rnk[l]];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
void update(int x,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
{
val[x]--;
tag[x]++;
return;
}
pushdown(x);
int mid=(l+r)>>1;
if(ql<=mid)
{
update(x<<1,l,mid,ql,qr);
}
if(qr>mid)
{
update(x<<1|1,mid+1,r,ql,qr);
}
pushup(x);
}
int query(int x,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
{
return val[x];
}
pushdown(x);
int mid=(l+r)>>1,res=1e9;
if(ql<=mid)
{
res=query(x<<1,l,mid,ql,qr);
}
if(qr>mid)
{
res=min(res,query(x<<1|1,mid+1,r,ql,qr));
}
return res;
}
bool check_chain(int u,int v)
{
while(top[u]!=top[v])
{
if(!query(1,1,n,dfn[top[v]],dfn[v]))
{
return 0;
}
v=fa[top[v]];
}
if(!query(1,1,n,dfn[u],dfn[v]))
{
return 0;
}
return 1;
}
void update_chain(int u,int v)
{
while(top[u]!=top[v])
{
update(1,1,n,dfn[top[v]],dfn[v]);
v=fa[top[v]];
}
update(1,1,n,dfn[u],dfn[v]);
}
int main()
{
freopen("fake.in","r",stdin);
freopen("fake.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>c[i];
}
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
addEdge(u,v);
addEdge(v,u);
}
dfs(1,0);
redfs(1,1);
build(1,1,n);
for(int i=1;i<=m;i++)
{
cin>>q[i].first>>q[i].second;
if(dep[q[i].first]>dep[q[i].second])
{
swap(q[i].first,q[i].second);
}
}
sort(q+1,q+m+1,cmp);
int ans=0;
for(int i=1;i<=m;i++)
{
if(check_chain(q[i].first,q[i].second))
{
ans++;
update_chain(q[i].first,q[i].second);
}
}
cout<<ans<<"\n";
return 0;
}
D. 基础图论练习题
too hard, gugugu.
2023.10.13 NOIP2023-div2模拟赛18
A. 橙子
通过打表 / 感性理解 / 推柿子可以发现,偶数长度一定为 \(0\),奇数 \(l,l+2,\cdots,r\) 为 \(1\),用树状数组维护下标奇偶性前缀和即可。
点击查看代码
#include <bits/stdc++.h>
// #define int long long
#define F first
#define S second
#define pb push_back
#define mpr make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
const int N=2e5+10;
int n,q,a[N];
struct FenwickTree
{
int tree[N];
void update(int x,int v)
{
while(x<=n)
{
tree[x]^=v;
x+=x&-x;
}
}
int query(int x)
{
int res=0;
while(x>0)
{
res^=tree[x];
x-=x&-x;
}
return res;
}
}T[2];
signed main()
{
freopen("orange.in","r",stdin);
freopen("orange.out","w",stdout);
scanf("%d%d",&n,&q);
rep(i,1,n)
{
scanf("%d",a+i);
T[i&1].update(i,a[i]);
}
while(q--)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(op==1)
{
T[x&1].update(x,a[x]);
T[x&1].update(x,y);
a[x]=y;
}
else
{
if((y-x+1)%2==0)
{
printf("0\n");
}
else
{
int ans=(T[x&1].query(y)^T[x&1].query(x-1));
printf("%d\n",ans);
}
}
}
return 0;
}
B. 保护
原题可以转化为:将 \(a_i\) 从大到小排序,每个 \(a_i\) 对应一个 \((0,0)-(i,a_i)\) 的矩形,求选出 \(m\) 个矩形的最小面积并。
最暴力的 DP 是 \(O(n^2m)\) 的,用斜率优化一下就可以变成 \(O(nm)\) 了。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define F first
#define S second
#define pb push_back
#define mpr make_pair
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e3+10;
const int INF=1e12+10;
int n,m,a[N];
int dp[N][N];
int stk[N],top=0;
void solve()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",a+i);
}
sort(a+1,a+n+1);
reverse(a+1,a+n+1);
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
dp[i][j]=INF;
}
}
dp[0][0]=0;
for(int i=1;i<=m;i++){
top=0;
stk[0]=0;
for(int j=1;j<=n;j++){
while(top>0&&dp[i-1][stk[top]]-stk[top]*a[j]>dp[i-1][stk[top-1]]-stk[top-1]*a[j])top--;
dp[i][j]=dp[i-1][stk[top]]+(j-stk[top])*a[j];
while(top>0&&(dp[i-1][stk[top]]-dp[i-1][j])*(stk[top]-stk[top-1])>(dp[i-1][stk[top-1]]-dp[i-1][stk[top]])*(j-stk[top]))top--;
stk[++top]=j;
}
}
int ans=INF;
for(int i=m;i<=n;i++)ans=min(ans,dp[m][i]);
printf("%lld\n",ans);
}
signed main()
{
freopen("protect.in","r",stdin);
freopen("protect.out","w",stdout);
int T;
scanf("%lld",&T);
while(T--)solve();
return 0;
}
C. Wall
经典线段树。维护区间 \(\max\) 和 \(\min\) 的 tag,具体 pushdown
见代码。
点击查看代码
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
const int INF = 0x3f3f3f3f;
int n, m;
int mx[N << 2], mn[N << 2];
void tagMax(int x, int v) {
mx[x] = max(mx[x], v);
mn[x] = max(mn[x], v);
}
void tagMin(int x, int v) {
mx[x] = min(mx[x], v);
mn[x] = min(mn[x], v);
}
void pushdown(int x) {
if(mx[x] > 0) {
tagMax(x << 1, mx[x]);
tagMax(x << 1 | 1, mx[x]);
mx[x] = 0;
}
if(mn[x] < INF) {
tagMin(x << 1, mn[x]);
tagMin(x << 1 | 1, mn[x]);
mn[x] = INF;
}
}
void updateMax(int x, int l, int r, int ql, int qr, int v) {
if(l >= ql && r <= qr) {
tagMax(x, v);
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if(ql <= mid) updateMax(x << 1, l, mid, ql, qr, v);
if(qr > mid) updateMax(x << 1 | 1, mid + 1, r, ql, qr, v);
}
void updateMin(int x, int l, int r, int ql, int qr, int v) {
if(l >= ql && r <= qr) {
tagMin(x, v);
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if(ql <= mid) updateMin(x << 1, l, mid, ql, qr, v);
if(qr > mid) updateMin(x << 1 | 1, mid + 1, r, ql, qr, v);
}
void print(int x, int l, int r) {
if(l == r) {
cout << mx[x] << "\n";
return;
}
pushdown(x);
int mid = (l + r) >> 1;
print(x << 1, l, mid);
print(x << 1 | 1, mid + 1, r);
}
int main() {
freopen("wall.in", "r", stdin);
freopen("wall.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(mn, 0x3f, sizeof(mn));
while (m--) {
int op, l, r, h;
cin >> op >> l >> r >> h;
++l, ++r;
if(op == 1) updateMax(1, 1, n, l, r, h);
else updateMin(1, 1, n, l, r, h);
}
print(1, 1, n);
return 0;
}
D. 控制
妙妙题。先考虑 \(n=2\) 的怎么做,发现一种用 priority_queue
维护的做法,推广到 \(n>2\) 的做法即可。详情见代码。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define F first
#define S second
#define pb push_back
#define mpr make_pair
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int B=23333,MOD=20190816170251;
int n,m,len[100010];
vector<int> a[100010];
priority_queue<pair<int,pair<int,int>>> pq;
bool cmp(vector<int> x,vector<int> y){
return x[1]-x[2]<y[1]-y[2];
}
signed main()
{
freopen("contain.in","r",stdin);
freopen("contain.out","w",stdout);
scanf("%lld%lld",&n,&m);
int sum=0;
for(int i=1;i<=n;i++){
scanf("%lld",len+i);
a[i].clear();
a[i].push_back(0);
for(int j=1;j<=len[i];j++){
int x;
scanf("%lld",&x);
a[i].push_back(x);
}
if(len[i]==1){
sum+=a[i][1];
i--;
n--;
continue;
}
sort(a[i].begin()+1,a[i].end(),greater<int>());
sum+=a[i][1];
}
if(!n){
printf("%lld\n",sum);
return 0;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)len[i]=(int)a[i].size()-1;
int ans=sum;
pq.push({sum+a[1][2]-a[1][1],{1,2}});
while(!pq.empty()&&(--m)){
int curS=pq.top().first,rowid=pq.top().second.first,colid=pq.top().second.second;
pq.pop();
ans=(ans*B%MOD+curS)%MOD;
if(colid<len[rowid]){
pq.push({curS+a[rowid][colid+1]-a[rowid][colid],{rowid,colid+1}});
}
if(rowid<n){
int nwS=curS+a[rowid+1][2]-a[rowid+1][1];
pq.push({nwS,{rowid+1,2}});
if(colid==2){
pq.push({nwS-a[rowid][2]+a[rowid][1],{rowid+1,2}});
}
}
}
printf("%lld\n",ans);
return 0;
}
2023.10.14 NOIP2023-div2模拟赛19
A. 超速
题意不清。垃圾题目。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 15, M = 1e5 + 10;
int n, v[N], l[N];
int m, a[M], f[M];
int q, L;
bool check(int x) {
if(x == m) return 1;
long double sum = 0.0;
for(int i = 1; i <= n; i++) sum += (long double)l[i] / (long double)(v[i] + a[x]);
return sum <= (long double)L;
}
signed main() {
freopen("speed.in", "r", stdin);
freopen("speed.out", "w", stdout);
scanf("%lld", &n);
for(int i = 1; i <= n; i++) scanf("%lld", v + i);
for(int i = 1; i <= n; i++) scanf("%lld", l + i);
scanf("%lld", &m);
for(int i = 1; i < m; i++) scanf("%lld", a + i);
for(int i = 1; i <= m; i++) scanf("%lld", f + i);
scanf("%lld", &q);
while(q--) {
int s, t;
scanf("%lld%lld", &s, &t);
L = t - s;
int l = 0, r = m;
while(l < r) {
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
printf("%lld\n", f[l]);
}
return 0;
}
B. 对常规的斗争
经典题。考虑每一个数作为区间中第一个同类的数的贡献,即求哪些区间以这个数作为第一个这一类的数。容易发现规律,二次差分即可。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, a[N], lst[N], c[N];
vector<int> vec;
signed main() {
freopen("fight.in", "r", stdin);
freopen("fight.out", "w", stdout);
scanf("%lld", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld", a + i);
vec.push_back(a[i]);
}
sort(vec.begin(), vec.end());
vec.resize(unique(vec.begin(), vec.end()) - vec.begin());
for(int i = 1; i <= n; i++) a[i] = lower_bound(vec.begin(), vec.end(), a[i]) - vec.begin() + 1;
for(int i = 1; i <= n; i++) {
c[1]++;
c[i - lst[a[i]] + 1]--;
c[n - i + 2]--;
c[i - lst[a[i]] + n - i + 2]++;
lst[a[i]] = i;
}
for(int i = 1; i <= n; i++) c[i] += c[i - 1];
for(int i = 1; i <= n; i++) c[i] += c[i - 1];
for(int i = 1; i <= n; i++) printf("%lld ", c[i]);
return 0;
}
C. 海报
考虑暴力的 dp。设 \(f(i,j)\) 表示考虑到 \(i\),已经连续选了 \(j\) 数了的最大和。因为是环,所以需要枚举最后一次选了多少个。
这个东西显然能用矩阵维护,线段树直接搞!
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e4 + 10;
int n, a[N], q;
int dp[N][4];
struct Matrix {
int a[4][4], N, M;
Matrix() {memset(a, -0x3f, sizeof(a));}
Matrix operator * (const Matrix &p) const {
Matrix res;
res.N = N, res.M = p.M;
for(int i = 0; i <= res.N; i++) {
for(int j = 0; j <= res.M; j++) {
for(int k = 0; k <= M; k++) {
res.a[i][j] = max(res.a[i][j], a[i][k] + p.a[k][j]);
}
}
}
return res;
}
void rset(int v) {
N = M = 3;
a[0][0] = a[1][0] = a[2][0] = a[3][0] = 0;
a[0][1] = a[1][2] = a[2][3] = v;
// for(int i = 0; i <= 3; i++) {
// for(int j = 0; j <= 3; j++) {
// cerr << a[i][j] << " ";
// }
// cerr << endl;
// }
}
} val[N << 2], emp;
struct SegmentTree {
void pushup(int x) {val[x] = val[x << 1] * val[x << 1 | 1];}
void build(int x, int l, int r) {
if(l == r) {
val[x].rset(a[l]);
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
pushup(x);
}
void update(int x, int l, int r, int pos, int v) {
if(l == r) {
val[x].rset(v);
return;
}
int mid = (l + r) >> 1;
if(pos <= mid) update(x << 1, l, mid, pos, v);
else update(x << 1 | 1, mid + 1, r, pos, v);
pushup(x);
}
Matrix query(int x, int l, int r, int ql, int qr) {
if(ql > qr) return emp;
if(l >= ql && r <= qr) return val[x];
int mid = (l + r) >> 1;
if(ql > mid) return query(x << 1 | 1, mid + 1, r, ql, qr);
if(qr <= mid) return query(x << 1, l, mid, ql, qr);
return query(x << 1, l, mid, ql, qr) * query(x << 1 | 1, mid + 1, r, ql, qr);
}
} T;
int calc() {
int res = 0;
for(int x = 0; x <= 3; x++) {
int sum = 0;
for(int i = n; i >= n - x + 1; i--) sum += a[i];
Matrix cur;
cur.N = 0, cur.M = 3;
cur.a[0][x] = sum;
cur = cur * T.query(1, 1, n, 1, n - x - 1);
for(int i = 0; i <= 3; i++) res = max(res, cur.a[0][i]);
// memset(dp, 0, sizeof(dp));
// dp[1][x + 1] = sum + a[1];
// dp[1][0] = sum;
// res = max(res, dp[1][x + 1]);
// for(int i = 2; i < n - x; i++) {
// int trans = 0;
// for(int j = 1; j <= 3; j++) dp[i][j] = dp[i - 1][j - 1] + a[i];
// dp[i][0] = 0;
// for(int j = 0; j <= 3; j++) dp[i][0] = max(dp[i][0], dp[i - 1][j]);
// for(int j = 0; j <= 3; j++) res = max(res, dp[i][j]);
// }
}
return res;
}
signed main() {
freopen("poster.in", "r", stdin);
freopen("poster.out", "w", stdout);
scanf("%lld", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld", a + i);
}
T.build(1, 1, n);
printf("%lld\n", calc());
scanf("%lld", &q);
while(q--) {
int pos, v;
scanf("%lld %lld", &pos, &v);
a[pos] = v;
T.update(1, 1, n, pos, v);
printf("%lld\n", calc());
}
return 0;
}
D. 机器人锦标赛
难度在题意理解/cf。可以发现前面一段的阈值填 \(m\) 一定可以,所以二分最多能填多少个 \(m\),然后再确定下一位即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m,s,z[N];
bool val[N<<1];
vector<int> a[N];
vector<pair<pair<int,int>,int>> vec[N];
int calc(){
int cnt=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)val[j]=(a[i][j]<z[j]);
for(int j=1;j<n;j++){
int x=vec[i][j-1].first.first,y=vec[i][j-1].first.second,op=vec[i][j-1].second;
if(op==1)val[n+j]=(val[x]&val[y]);
else val[n+j]=(val[x]|val[y]);
}
cnt+=val[n+n-1];
}
return cnt;
}
int main(){
freopen("robot.in","r",stdin);
freopen("robot.out","w",stdout);
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
for(int j=0;j<n-1;j++){
int a,b,op;
cin>>a>>b>>op;
vec[i].push_back(make_pair(make_pair(a,b),op));
}
}
for(int i=1;i<=m;i++){
a[i].push_back(0);
for(int j=1;j<=n;j++){
int x;cin>>x;
a[i].push_back(x);
}
}
int l=1,r=n+1;
while(l<r){
int mid=(l+r+1)>>1;
memset(z,0,sizeof(z));
for(int i=1;i<mid;i++)z[i]=m;
if(calc()<=s)l=mid;
else r=mid-1;
}
memset(z,0,sizeof(z));
for(int i=1;i<l;i++)z[i]=m;
int L=0,R=m;
while(L<R){
int mid=(L+R+1)>>1;
z[l]=mid;
if(calc()<=s)L=mid;
else R=mid-1;
}
z[l]=L;
for(int i=1;i<=n;i++)cout<<z[i]<<" ";
return 0;
}
gugugu 了好几天。
2023.10.18 NOIP2023-div2模拟赛22
A. 玛雅历
shaber 模拟,不做评价。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define rep(i, j, k) for (int i = (j); i <= (k); i++)
#define per(i, j, k) for (int i = (j); i >= (k); i--)
#define ll long long
#define vi vector<int>
#define sz(a) ((int)(a.size()))
#define mset(f, x) memset(f, x, sizeof(f))
#define ALL(x) (x).begin(), (x).end()
#define rALL(x) (x).rbegin(), (x).rend()
#define uni(x) x.resize(unique(ALL(x)) - x.begin())
#define ull unsigned long long
#define pii pair<int, int>
using namespace std;
const int Times[9] = {1, 20, 360, 7200, 144000, 2880000, 57600000, 1152000000, 23040000000};
const int Months[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int a[9];
bool is_leap_year(int Y) {return (Y % 4 == 0 && (Y % 100 || Y % 400 == 0));}
void solve() {
scanf("%lld.%lld.%lld.%lld.%lld.%lld.%lld.%lld.%lld", a + 8, a + 7, a + 6, a + 5, a + 4, a + 3, a + 2, a + 1, a + 0);
int days = 0;
rep(i, 0, 8) days += a[i] * Times[i];
int Y = (days / 146097) * 400 - 3113, M = 8, D = 11;
days %= 146097;
while(days >= 366) {
days -= 365;
Y++;
if(is_leap_year(Y)) days--;
}
while(days--) {
D++;
int x = Months[M];
if(M == 2 && is_leap_year(Y)) x++;
if(D > x) {
D = 1;
M++;
if(M > 12) {
M = 1;
Y++;
}
}
}
if(Y <= 0) printf("%lld %lld %lld BC\n", D, M, 1 - Y);
else printf("%lld %lld %lld\n", D, M, Y);
}
signed main() {
freopen("maya.in", "r", stdin);
freopen("maya.out", "w", stdout);
int cas;
scanf("%lld", &cas);
while(cas--) {
solve();
}
return 0;
}
B. 大融合
Hint: 二分 + DP + 单调队列优化。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define rep(i, j, k) for (int i = (j); i <= (k); i++)
#define per(i, j, k) for (int i = (j); i >= (k); i--)
#define ll long long
#define vi vector<int>
#define sz(a) ((int)(a.size()))
#define mset(f, x) memset(f, x, sizeof(f))
#define ALL(x) (x).begin(), (x).end()
#define rALL(x) (x).rbegin(), (x).rend()
#define uni(x) x.resize(unique(ALL(x)) - x.begin())
#define ull unsigned long long
#define pii pair<int, int>
using namespace std;
const int N = 1e3 + 10, INF = 0x3f3f3f3f3f3f3f3f;
int n, m, Q, d, c[N], Lim = 0;
int x[N][N], f[N][N], dp[N][N];
unordered_map<int, bool> app;
deque<pii> q[N][2], tmp[2];
bool check(int v) {
int L = max(1LL, d - v), R = min(n, d + v);
rep(i, 1, n) rep(j, 1, n) dp[i][j] = -INF; dp[1][1] = 0;
int ans = 0;
rep(i, 1, n) rep(t, 0, 1) while(!q[i][t].empty()) q[i][t].pop_back();
rep(i, 1, n) {
rep(t, 0, 1) while(!tmp[t].empty()) tmp[t].pop_back();
rep(j, 1, n) {
rep(t, 0, 1) {
while(!tmp[t].empty() && tmp[t].front().second < j - R) tmp[t].pop_front();
while(!q[j][t].empty() && q[j][t].front().second < j - R) q[j][t].pop_front();
}
if(j > L) {
int cur = x[i][j - L];
while(!tmp[cur].empty() && tmp[cur].back().first <= dp[i][j - L]) tmp[cur].pop_back();
tmp[cur].push_back(make_pair(dp[i][j - L], j - L));
}
if(i > L) {
int cur = x[i - L][j];
while(!q[j][cur].empty() && q[j][cur].back().first <= dp[i - L][j]) q[j][cur].pop_back();
q[j][cur].push_back(make_pair(dp[i - L][j], i - L));
}
rep(t, 0, 1) {
int delta = f[i][j] + (x[i][j] == t);
if(!tmp[t].empty()) dp[i][j] = max(dp[i][j], tmp[t].front().first + delta);
if(!q[j][t].empty()) dp[i][j] = max(dp[i][j], q[j][t].front().first + delta);
}
ans = max(ans, dp[i][j]);
}
}
return Lim <= ans;
}
signed main() {
freopen("harbinbeer.in", "r", stdin);
freopen("harbinbeer.out", "w", stdout);
scanf("%lld %lld %lld %lld", &n, &m, &Q, &d);
rep(i, 1, m) {
scanf("%lld", c + i);
int pw = 1;
while(pw <= c[i]) {
app[c[i] % (pw * 10)] = 1;
pw *= 10;
}
}
while(Q--) {
int l, b, w;
scanf("%lld %lld %lld", &l, &b, &w);
if(!app[b]) Lim += w;
}
rep(i, 1, n) rep(j, 1, n) scanf("%lld", &x[i][j]), x[i][j]--;
rep(i, 1, n) rep(j, 1, n) scanf("%lld", &f[i][j]);
if(!check(n)) {
printf("-1\n");
return 0;
}
int l = 0, r = n;
while(l < r) {
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
} printf("%lld\n", l);
return 0;
}
C. 好 ♂ 朋 ♂ 友
性质题。
观察到对于每个左端点,区间 \(\texttt{OR}\) 的值只有可能有 \(O(\log V)\) 个。
那就区间离线下来,线段树维护即可。
upd:我好像是我们年级这一题唯一没有被卡常的!!!
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define rep(i, j, k) for (int i = (j); i <= (k); i++)
#define per(i, j, k) for (int i = (j); i >= (k); i--)
#define ll long long
#define vi vector<int>
#define sz(a) ((int)(a.size()))
#define mset(f, x) memset(f, x, sizeof(f))
#define ALL(x) (x).begin(), (x).end()
#define rALL(x) (x).rbegin(), (x).rend()
#define uni(x) x.resize(unique(ALL(x)) - x.begin())
#define ull unsigned long long
#define pii pair<int, int>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int n, m, k, a[N], st[N][20], ans[M];
bool can[10];
struct SegmentTree {
int sum[N << 2], tag[N << 2];
void pushup(int x) {sum[x] = sum[x << 1] + sum[x << 1 | 1];}
void pushdown(int x, int l, int r) {
if(!tag[x]) return;
int mid = (l + r) >> 1;
sum[x << 1] += tag[x] * (mid - l + 1);
sum[x << 1 | 1] += tag[x] * (r - mid);
tag[x << 1] += tag[x];
tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
void update(int x, int l, int r, int ql, int qr) {
if(l >= ql && r <= qr) {
sum[x] += r - l + 1;
tag[x]++;
return;
}
pushdown(x, l, r);
int mid = (l + r) >> 1;
if(ql <= mid) update(x << 1, l, mid, ql, qr);
if(qr > mid) update(x << 1 | 1, mid + 1, r, ql, qr);
pushup(x);
}
int query(int x, int l, int r, int ql, int qr) {
if(l >= ql && r <= qr) return sum[x];
pushdown(x, l, r);
int mid = (l + r) >> 1, res = 0;
if(ql <= mid) res = query(x << 1, l, mid, ql, qr);
if(qr > mid) res += query(x << 1 | 1, mid + 1, r, ql, qr);
return res;
}
} T;
struct Node {
int id, l, r;
bool operator < (const Node &p) const {
return r < p.r;
}
} q[M];
signed main() {
freopen("friend.in", "r", stdin);
freopen("friend.out", "w", stdout);
scanf("%lld %lld %lld", &n, &m, &k);
rep(i, 1, k) {
int x;
scanf("%lld", &x);
can[x] = 1;
}
rep(i, 1, n) {
scanf("%lld", a + i);
st[i][0] = (a[i] | a[i - 1]);
}
rep(j, 1, 19) {
rep(i, 1, n) {
st[i][j] = (st[i][j - 1] | st[max(0LL, i - (1 << (j - 1)))][j - 1]);
}
}
rep(i, 1, m) {
scanf("%lld %lld", &q[i].l, &q[i].r);
q[i].id = i;
}
sort(q + 1, q + m + 1);
q[0].r = 0;
rep(i, 1, m) {
rep(j, q[i - 1].r + 1, q[i].r) {
int pos = j, val = a[j];
while(pos >= 1) {
int nxt = pos;
per(k, 19, 0) {
if(nxt > (1 << k)) {
if((val | st[nxt][k]) != val) continue;
nxt -= (1 << k);
}
}
if(can[val % 10]) T.update(1, 1, n, nxt, pos);
pos = nxt - 1, val |= a[pos];
}
}
ans[q[i].id] = T.query(1, 1, n, q[i].l, q[i].r);
}
rep(i, 1, m) printf("%lld\n", ans[i]);
return 0;
}
D. 图的直径
gugugu.
本文来自博客园,作者:Jerry_Jiang,转载请注明原文链接:https://www.cnblogs.com/Jerry-Jiang/p/17711661.html