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<bits/stdc++.h>
#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])<eps) return g[a]>g[b];
	 return p[a]<p[b];
}
void Input()
{
	 scanf("%lld%lld",&n,&t);
	 for(int i=1;i<=n;i++)
	 {
	 	 scanf("%lf%lf%lf",&a[i],&b[i],&p[i]);
	 	 g[i]=a[i]*p[i],id[i]=i;
	 	 Max=max(Max,b[i]*p[i]);
	 }
}
Mat create(int x)
{
	Mat res;
	res.Init();
	res.jz[0][0]=1-p[x];
	res.jz[1][0]=p[x]*Max;
	res.jz[1][1]=res.jz[2][2]=res.jz[2][1]=1;
	res.jz[2][0]=g[x];
	return res;
}
Mat mul(Mat a,Mat b)
{
	Mat res;
	res.Init();
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++)
		{
			for(int k=0;k<3;k++)
			{
				res.jz[i][j]+=a.jz[i][k]*b.jz[k][j];
			}
		}
	}
	return res;
}
void Eternal_Battle()
{
 	 sort(id+1,id+1+n,cmp);
     for(int i=1;i<=n;i++)
     {
     	 int now=id[i];
     	 int pre=id[i-1];
     	 if(fabs(p[now]-p[pre])<eps) continue;
     	 while(top>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<t)
	 {
	 	  while(poi<top&&(Y(q[poi+1])-Y(q[poi]))>-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<<i)>=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<<i);
		  }
		  res=mul(res,my[0]);++now;
		  f=res.jz[0][1]*Max-res.jz[0][0];
	 }
	 printf("%.7f",res.jz[0][0]);
}
void Output()
{
	 ;
}
signed main()
{
	Input();
    Eternal_Battle();
    Output();
	return (0^0);
}

\(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<bits/stdc++.h>
#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 <bits/stdc++.h>
#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-r<eps) swap(l,r);
			else return false;
		}
		dx=poz[i][0];
		dy=poz[i][1];
	}
	return true;
}
void Input()
{
	scanf("%d%lf%lf%lf%lf",&n,&w,&h,&e1,&e2);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%lf%lf",&t[i],&poz[i][0],&poz[i][1]);
	}
}
void Eternal_Battle()
{
	if(!check(w+1))
	{
	   cout<<"-1"<<endl;
	   return ;
	}
	double l=0,r=w,mid;
	while((r-l)/max(1.0,l)>1e-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);
}
posted @ 2022-04-02 17:58  Authentic_k  阅读(32)  评论(0编辑  收藏  举报