DTOJ 2022.11.08 测试 题解
A 光
题目大意
有四个格子,左上、右上、左下、右下分别可以填一个值
题解
1.
直接枚举四个.(时间复杂度中的
2.
只需要枚举三个,算出每个格子还需要多少,剩下的一个就可以
3.
二分答案,然后枚举对角线上的点,这时候可以算出剩下的两个格子还需要多少,算出大致的值可以上下枚举一下.
4.
(准确来说是
考场上不知道为什么想到了这个神奇做法(感觉还是很可行的)
注意到限制是一系列的线性不等式,可以考虑线性规划()
(普通的线性规划其实就是二元一次不等式组,然后求
改成四元不等式其实差不多,四个方程一定只有一个解,所以一定也只有一个顶点,这个时候一定取到最小值.
但是我们注意到
最后求出来之后也跟刚才一样上下枚举一下.
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1505;
const int D = 10;
double a[6][6],b[6],B[6];
int res[6];
int ans=N<<2;
void solve()
{
for(int i=1; i<=4; i++)
{
int mnp=1;
for(int j=1; j<=4; j++) if(a[j][i]>a[mnp][i]) mnp=j;
for(int j=1; j<=4; j++)
if(j!=mnp)
{
double d=a[j][i]/a[mnp][i];
for(int k=1; k<=4; k++) a[j][k]-=a[mnp][k]*d;
b[j]-=b[mnp]*d;
}
}
for(int i=1; i<=4; i++) res[i]=(int)b[i]/a[i][i];
for(int i=max(res[1]-D,0); i<=res[1]+D; i++) for(int j=max(res[2]-D,0); j<=res[2]+D; j++)
for(int k=max(res[3]-D,0); k<=res[3]+D; k++) for(int l=max(res[4]-D,0); l<=res[4]+D; l++)
if(i+j/2+k/2+l/4>=B[1] and j+i/2+l/2+k/4>=B[2] and l+j/2+k/2+i/4>=B[4] and k+i/2+l/2+j/4>=B[3])
if(i+j+k+l<ans) ans=i+j+k+l;
}
int main()
{
for(int i=1; i<=4; i++) scanf("%lf",&B[i]);
for(int t=0; t<16; t++)
{
for(int i=1; i<=4; i++) b[i]=B[i];
for(int i=1; i<=4; i++) for(int j=1; j<=4; j++) a[i][j]=0.5;
for(int i=1; i<=4; i++) a[i][i]=1,a[i][5-i]=0.25;
for(int j=1; j<=4; j++) if((t>>(j-1))&1)
{
for(int i=1; i<=4; i++) a[j][i]=0;
b[j]=0; a[j][j]=1;
}
solve();
}
printf("%d\n",ans);
return 0;
}
B 奶茶代金券
还没补题,看着像简单贪心,但细节实在太多了(((((((ww不会写贪心)
C 函数复合
题面
给定
; ; 。
给定
:表示令 ; :表示令 ; :表示令 ; :表示询问 的值,其中 表示函数 与 的复合,即 。
请维护以上操作。
对于所有测试数据,保证
题解
这题我测试的时候写得很顺利,对拍都过了,不知道为什么交上去只有 75 分,把 assert 删掉了之后就过了(我直接问号?
呃呃所以怎么写呢
方法1
注意到无论多少次
注意到这样函数的复合满足结合律,所以你可以直接在线段树上维护函数,难点主要是怎么合并两个函数
这是我的写法(我对于一个函数维护了它的最小值,最大值和中间那段线的纵截距
friend Kurumi operator^ (Kurumi f, Kurumi g) // Kurumi 是一个函数结构体 (Kurumi 不是狂三是胡桃)
{
Kurumi res;
res.ymx=g.work(f.ymx);
res.ymn=g.work(f.ymn); //结果的最小值一定是这样的
if(res.ymx==res.ymn) { res.b=0; return res; }
int rxmn=f.xmn(); int rxmx=f.xmx();
int t1=f.inv(g.xmn()),t2=f.inv(g.xmx());
if(t1!=-1 and t1>rxmn) rxmn=t1;
if(t2!=-1 and t2<rxmx) rxmx=t2;
res.b=res.ymx-rxmx;
return res;
}
最后代码贴上来:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e5+5, inf = 1e9;
int n,q;
struct Kurumi
{
int ymn,ymx,b;
int xmn() { return ymn-b; }
int xmx() { return ymx-b; }
int work(int x)
{
if(x<=xmn()) return ymn;
else if(x>=xmx()) return ymx;
else return x+b;
}
int inv(int y)
{
if(y<ymn or y>ymx) return -1;
else return y-b;
}
void print()
{
printf("ymn=%d ymx=%d b=%d\n",ymn,ymx,b);
}
friend Kurumi operator^ (Kurumi f, Kurumi g)
{
// printf("f: "),f.print();
// printf("g: "),g.print();
Kurumi res;
res.ymx=g.work(f.ymx);
res.ymn=g.work(f.ymn);
if(res.ymx==res.ymn) { res.b=0; return res; }
int rxmn=f.xmn(); int rxmx=f.xmx();
int t1=f.inv(g.xmn()),t2=f.inv(g.xmx());
if(t1!=-1 and t1>rxmn) rxmn=t1;
if(t2!=-1 and t2<rxmx) rxmx=t2;
//assert(rxmx-rxmn==res.ymx-res.ymn);
res.b=res.ymx-rxmx;
return res;
}
} fc[N];
struct Sagiri
{
int l,r;
Kurumi dat;
} t[N<<2];
#define ls (p<<1)
#define rs (p<<1)|1
void build(int p, int l, int r)
{
// printf("%d [%d %d]\n",p,l,r);
t[p].l=l,t[p].r=r;
if(l==r) { t[p].dat=fc[l]; return ; }
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
t[p].dat=t[ls].dat^t[rs].dat;
}
void change(int p, int x, const Kurumi &v)
{
if(t[p].l==t[p].r) { t[p].dat=v; return ; }
int mid=(t[p].l+t[p].r)>>1;
if(x<=mid) change(ls,x,v);
else if(x>mid) change(rs,x,v);
t[p].dat=t[ls].dat^t[rs].dat;
}
Kurumi query(int p, int l, int r)
{
if(t[p].l==l and t[p].r==r) return t[p].dat;
int mid=(t[p].l+t[p].r)>>1;
if(r<=mid) return query(ls,l,r);
else if(l>mid) return query(rs,l,r);
else return query(ls,l,mid)^query(rs,mid+1,r);
}
int main()
{
scanf("%d",&n);
for(int i=1,op,w; i<=n; i++)
{
scanf("%d%d",&op,&w);
if(op==1) fc[i]={0,inf,w};
else if(op==2) fc[i]={0,w,0};
else fc[i]={w,inf,0};
}
build(1,1,n);
scanf("%d",&q);
for(int i=1,op,p,w; i<=q; i++)
{
scanf("%d",&op);
if(op==4)
{
scanf("%d",&w);
Kurumi res=query(1,1,n);
printf("%d\n",res.work(w));
}
else
{
scanf("%d%d",&p,&w);
if(op==1) change(1,p,{0,inf,w});
else if(op==2) change(1,p,{0,w,0});
else change(1,p,{w,inf,0});
}
}
return 0;
}
方法2
注意到无论多少次
两个这种函数也具有可合并性,线段树上维护
D 积木拼接
题面大意
题解
注意摆成一个环的话,
会发现,一个环上一定会有最小和最大的
也是可以取得到等的,所以就变成求
按
先枚举
这东西怎么优化呢,这又是一个小技巧哦
根据红日学长,这题具有决策单调性.
是的决策单调性并不一定是 dp 才会出现哒)φ(>ω<*)
我们通过打表找规律,或者做题经验,或者有做题经验的学长的锐评,可以得知对于每个
于是我们考虑分治(?红日学长说是一个套路那我就记下来吧!)
怎么分治呢
我们要求
我们可以先暴力求出
那么我们可以使用可持久化线段树(主席树)求出任意区间前
可持久化线段树的学习笔记 还没补(!!) 补完了(`・ω・´)
然后分治求
因为分治最多就
分治每一次就记录一下
void work(int l, int r, int lb, int rb)
{
if(l>r) return ;
int mid=(l+r)>>1;
f[mid]=-inf;
for(int i=max(lb,mid+m-1); i<=rb; i++)
{
ll x=query_sumk(rt[i],rt[mid-1],1,M,m)-((p[i].h-p[mid].h)<<1); //就是那个式子
if(x>f[mid]) f[mid]=x,R[mid]=i; //更新 ans[mid] 和 R[mid]
}
work(l,mid-1,lb,R[mid]),work(mid+1,r,R[mid],rb); // 递归下去找
}
整个的代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+5, M = 1e9+5;
const ll inf = 1e18;
int m,n,tot;
struct Misaka { int v,h; } p[N];
int R[N];
ll f[N];
struct Sagiri { int ls,rs,cnt; ll dat; } t[N<<5];
int rt[N];
void change(int &p, int q, ll l, ll r, int x)
{
t[p=++tot]=t[q];
if(l==r) { t[p].cnt++; t[p].dat+=l; return ; }
ll mid=(l+r)>>1;
if(x<=mid) change(t[p].ls,t[q].ls,l,mid,x);
else change(t[p].rs,t[q].rs,mid+1,r,x);
t[p].dat=t[t[p].ls].dat+t[t[p].rs].dat;
t[p].cnt=t[t[p].ls].cnt+t[t[p].rs].cnt;
}
ll query_sumk(int p, int q, ll l, ll r, int k)
{
if(l==r) return (ll)l*k;
ll mid=(l+r)>>1;
int rcnt=t[t[p].rs].cnt-t[t[q].rs].cnt;
if(k<=rcnt) return query_sumk(t[p].rs,t[q].rs,mid+1,r,k);
else return query_sumk(t[p].ls,t[q].ls,l,mid,k-rcnt)+t[t[p].rs].dat-t[t[q].rs].dat;
}
void work(int l, int r, int lb, int rb)
{
if(l>r) return ;
int mid=(l+r)>>1;
f[mid]=-inf;
for(int i=max(lb,mid+m-1); i<=rb; i++)
{
ll x=query_sumk(rt[i],rt[mid-1],1,M,m)-((p[i].h-p[mid].h)<<1);
if(x>f[mid]) f[mid]=x,R[mid]=i;
}
work(l,mid-1,lb,R[mid]),work(mid+1,r,R[mid],rb);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d%d",&p[i].v,&p[i].h);
sort(p+1,p+1+n, [&] (const Misaka &x, const Misaka &y) { return x.h<y.h; });
for(int i=1; i<=n; i++) change(rt[i],rt[i-1],1,M,p[i].v);
work(1,n-m+1,m,n);
ll res=-inf;
for(int i=1; i<=n-m+1; i++) res=max(res,f[i]);
printf("%lld\n",res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!