1. 程式人生 > 其它 >4.2省選模擬

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),\)然後我們和一個線段求出了一個可行域,繼續擴散求可行域就好了,每次判斷是否有交點

一開始是一個點,考慮擴散,形成一個正方形,一個線段,每個點都擴散一個正方形,形成了一個六邊形,簡單計算即可

#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();
}