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