4.2省选模拟
好吧,在沉迷了一周之后,今天终于找回状态了,抗压能力又增强了不少,呵
今天的天气不错捏\(\sim\)
\(T1\)
\(dp\)起手,以示尊敬
上来误以为是个背包,预处理出代价,观察一下范围,\(t<=1e10,\)必然需要矩阵加速,那么\(dp\)第二维状态不能是\(t,\)那么考虑第一维状态设置\(t,\)状态易得
\(dp[i]\)表示剩下\(i\)时刻且没有升级过的期望收益,我们可以根据第一次升级的时间将所有的状态分类
比较贪心的思考一下,我们一次升级机会肯定要狂刷\(p_i\times b_i\)最大的游戏(一开始题意没读懂,我以为只是升级自己的游戏)
我想,最大的期望不是一开始挑最大\(a_i\times b_i\)转移就好了嘛,并不是,我们每一步决策导致后面的期望是不一样的,那么就可以每次找出最优点决策了
转移易得
\(f_i=\max(p_j\times (a_j+(i-1)M)+(1-p_j)f_{i-1})\)
对于一维\(dp,\)不优化一下就是不尊敬吧
\(f_i=\max(p_j\times((i-1)M-f_{i-1})+p_ja_j)+f_{i-1}\)
这是一个斜率式,\(s_i=(iM-f_i)\)
\(f_i=\max(s_{i-1}p_j+p_ja_j)+f_{i-1}\)
对于每一个\(j\)是决策点,我们决策是单调递增(从小到大按\(p_i,a_ip_i\)排序)
这样的复杂度是\(O(n+t)\)
发现我们可以每次转移一部分切点,那么我们一次性转移完所有切点可以使用矩阵
有了转移式子,转移矩阵就很好说了
二分切点变化位置\(+\)矩阵快速幂可以达到\(O(nlog^2t)\)我觉得可以过了吧,极限\(100000\times 33\times 33=1e8(3s)\)只要评测机快亿点
话说,我是今天下午才彻底明白斜率优化的\(?\)原来只会机械的推式子.
今日一乐:读入\(ll\)使用\(\%d\)大于\(INTMAX\)自动保留到\(INTMAX\)
#define Eternal_Battle ZXK
#include
#define int long long
#define MAXN 100005
using namespace std;
const double eps=1e-14;
int n,t,q[MAXN],id[MAXN],top;
double a[MAXN],b[MAXN],p[MAXN],g[MAXN],Max;
struct Mat
{
double jz[4][4];
void Init()
{
memset(jz,0,sizeof(jz));
}
}my[50];
double X(int x)
{
return p[x];
}
double Y(int x)
{
return g[x];
}
bool cmp(int a,int b)
{
if(fabs(p[a]-p[b])g[b];
return p[a]1&&(Y(now)-Y(q[top]))*(X(q[top])-X(q[top-1]))>(Y(q[top])-Y(q[top-1]))*(X(now)-X(q[top]))) top--;
q[++top]=now;
}
int now=0,poi=1;//时间和指针
double f=0;
Mat res;
res.Init();
res.jz[0][2]=1;
while(now-f*(X(q[poi+1])-X(q[poi]))) poi++;
my[0]=create(q[poi]);
for(int i=1;i<40;i++)
{
my[i]=mul(my[i-1],my[i-1]);
}
for(int i=39;i>=0;i--)
{
if(now+(1ll<=t) continue;
Mat G=mul(res,my[i]);
double S=G.jz[0][1]*Max-G.jz[0][0];
if(poi==top||Y(q[poi])-Y(q[poi+1])>-S*(X(q[poi])-X(q[poi+1]))) res=G,now+=(1ll<
\(T2\)
考场一下想到这莫不是个倒推\(?\)手玩一下样例,猜测只和奇偶性有关
好,然后猜对了(一半,)然后输出和答案有一半是错的
继续分析,发现可以原地跳,大概思想就是分析一下每个格子是先手必胜或是先手必败就好了,还是倒推
分析,如果跳出去\(1\sim m\)步存在先手必败,那么先手必胜,否则观察奇偶性
然后我上午貌似是细节寄了\(QAQ\)
我查了下我上午代码
$!\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ !\ \ $
\(CCCCCCCCCCCCCCCCCCCCCCCCCCCCC,\)我\(TM\)忘记减\(1\)了(没读题)
\(wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb,wssb\)
其实这样就很明显了,由于可以递推,这个可以写成\(ddp,m\)很小,矩阵大小有保证,呵呵,我亲手断送我的\(100pts\)
#define Eternal_Battle ZXK
#include
#define int long long
#define rs ((x<<1)|1)
#define MAXN 200100
#define ls (x<<1)
using namespace std;
int a[MAXN],n,m,q;
struct Mat
{
int jz[10];
Mat()
{
memset(jz,0,sizeof(jz));
}
};
Mat merge(Mat x,Mat y)
{
Mat res;
for(int i=1;i<=m+1;i++)
{
res.jz[i]=y.jz[x.jz[i]];
}
return res;
}
struct node
{
int l,r,laz;
Mat v[2];
}tr[MAXN<<2];
void push_up(int x)
{
tr[x].v[0]=merge(tr[x<<1|1].v[0],tr[x<<1].v[0]);
tr[x].v[1]=merge(tr[x<<1|1].v[1],tr[x<<1].v[1]);
}
void push_down(int x)
{
if(tr[x].laz)
{
swap(tr[x<<1].v[0],tr[x<<1].v[1]);
tr[x<<1].laz^=1;
swap(tr[x<<1|1].v[0],tr[x<<1|1].v[1]);
tr[x<<1|1].laz^=1;
tr[x].laz=0;
}
}
void build(int x,int l,int r)
{
tr[x].l=l,tr[x].r=r;
if(l==r)
{
for(int i=1;i<=m;i++)
{
tr[x].v[a[l]].jz[i]=tr[x].v[a[l]^1].jz[i]=i+1;
}
tr[x].v[a[l]].jz[m+1]=1;
tr[x].v[a[l]^1].jz[m+1]=m+1;
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
push_up(x);
}
void change(int x,int L,int R)
{
int l=tr[x].l,r=tr[x].r;
if(L<=l&&r<=R)
{
swap(tr[x].v[0],tr[x].v[1]);
tr[x].laz^=1;
return;
}
push_down(x);
int mid=(l+r)>>1;
if(R<=mid) change(ls,L,R);
else if(L>mid) change(rs,L,R);
else change(ls,L,mid),change(rs,mid+1,R);
push_up(x);
}
Mat query(int x,int L,int R)
{
int l=tr[x].l,r=tr[x].r;
if(L<=l&&r<=R) return tr[x].v[0];
push_down(x);
int mid=(l+r)>>1;
if(R<=mid) return query(x<<1,L,R);
else if(L>mid) return query(x<<1|1,L,R);
else return merge(query(x<<1|1,mid+1,R),query(x<<1,L,mid));
}
void Input()
{
scanf("%lld%lld%lld",&n,&m,&q);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]=(a[i]+1)%2;
}
}
void Eternal_Battle()
{
build(1,1,n);
while(q--)
{
int opt,l,r;
scanf("%lld%lld%lld",&opt,&l,&r);
if(opt==1)
{
int x;
scanf("%lld",&x);
if(x&1) change(1,l,r);
}
else
{
Mat t=query(1,l,r);
cout<<1+(t.jz[m+1]==1)<<"\n";
}
}
}
void Output()
{
;
}
signed main()
{
Input();
Eternal_Battle();
Output();
return (0^0);
}
还有啊,\(DDP\)不一定写在树上
\(T3\)
计算几何,\(dog\)都不写,好好好,\(I\ am \ dog\)
假设\((a,0)(x_i,y_i)(b,h)\)共线,我们求出来的\(a,b\)满足一次函数关系
一开始我们是一个点\((e_1,e_2),\)然后我们和一个线段求出了一个可行域,继续扩散求可行域就好了,每次判断是否有交点
一开始是一个点,考虑扩散,形成一个正方形,一个线段,每个点都扩散一个正方形,形成了一个六边形,简单计算即可
由于一些奇怪原因,只能在\(C++17\)才能过\(?\)貌似哪里\(UB\)了,\(QAQ\)。
#define Eternal_Battle ZXK
#include
#define MAXN 100005
using namespace std;
const double eps=1e-8;
int n,t[MAXN];
double w,h,e1,e2,poz[MAXN][2];
bool check(double v)
{
double l=e1,r=e1,dx=(e1+e2)/2,dy=h/2;
for(int i=1;i<=n;i++)
{
double a=(h-poz[i][1])/poz[i][1]*(dy/(h-dy)),b=(dx+dy/(h-dy)*dx)+(-dy/(h-dy))*(poz[i][0]+(h/poz[i][1]-1)*poz[i][0]);
double a1=v*(t[i]-t[i-1]),a2=(dy/(h-dy))*a1;
double l1=l-a1,r1=r+a1,l2=(l-a2-b)/a,r2=(r+a2-b)/a,l3,r3,l4,r4;
if(a<1-eps)
{
l3=(a1+a2-b)/(a-1);
r3=(-a1-a2-b)/(a-1);
}
else if(a>1+eps)
{
l3=(-a1-a2-b)/(a-1);
r3=(a1+a2-b)/(a-1);
}
else
{
if(abs(b)>a1+a2)return false;
l3=0,r3=w;
}
l4=poz[i][0]-(w-poz[i][0])*poz[i][1]/(h-poz[i][1]);
r4=poz[i][0]+poz[i][0]*poz[i][1]/(h-poz[i][1]);
l=max(max(max(l1,l2),max(l3,l4)),0.0);
r=min(min(min(r1,r2),min(r3,r4)),w);
if(l>r)
{
if(l-r1e-8)
{
mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.12f\n",(double)(l+r)/2);
}
void Output()
{
;
}
int main()
{
Input();
Eternal_Battle();
Output();
return (0^0);
}