最近细节总是挂总是挂总是挂。我说我$T2$估分$62$有人信吗?
有没有人信不重要。反正是没拿到。而且直接爆零了。
原因是变量名写反了且少了一种情况(想到了忘写了)。这怕不是个弱智吧。。。
大样例看起来挺大的,然后都跑过了,我以为不说$62$,$48$也应该是稳了。
时间分配还是奇奇怪怪的,反正就是莫名的时间不够。
$T2$研究了好久,$T3$也想了挺长时间。
然后一直觉得$T1$很神仙(看数据范围不像什么常规的多项式复杂度$dp$,没往状压上想)于是最后只给它留了半小时左右。
然后发现,好像出题人说它是签到题这是真的,于是就开始写,发现复杂度好像有点高(然而其实已经$AC$了)
于是剩下的时间都拿去剪枝卡常什么的,再也没有看一眼我爆零的$T2...$
改题的时候又自闭了。因为有下发数据于是一直没有写对拍,但是下发数据又太大,于是一直干瞪着。
然后问别人也大都不给我看于是磨磨蹭蹭将近$6$小时就过去了。
结果最后还是对拍小数据发现了问题,然后就$A$掉了。
考后对拍也是真有用啊。。。
T1:Max
大意:初始为空的序列$A$长为$n$。$m$轮操作第$i$轮有$p_{i,j,k}$的概率使得$A_j + = k$。其中$0le k le c$
求所有操作后整个序列最大值的期望。$n le 40,c'le 3 ,m le 10$
数据范围小的奇怪。$m$在状压范围内。
于是就状压$m$表示这个子集的轮是否已经被分配给其它人。做一个背包$dp$就行了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 int n,m,c,p[55][11][5],dp[1025][33],bp[1025][33],t[33],ans,bt[1025],pre[1025][33]; 5 int main(){//freopen("max3.in","r",stdin); 6 cin>>n>>m>>c; 7 for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)for(int k=0;k<=c;++k)scanf("%d",&p[j][i][k]); 8 const int mst=(1<<m)-1; 9 for(int i=1;i<=mst;++i)bt[i]=bt[i^i&-i]+c; 10 dp[mst][0]=1; 11 for(int i=1;i<=n;++i){ 12 memset(bp,0,sizeof bp);memset(pre,0,sizeof pre); 13 for(int s=mst;~s;--s){ 14 bp[s][0]=1; 15 for(int j=1;j<=m;++j)if(s&1<<j-1){ 16 for(int y=0;y<=bt[s];++y)for(int x=0;x<=c;++x)t[y+x]=(t[y+x]+1ll*bp[s][y]*p[i][j][x])%mod; 17 for(int y=0;y<=bt[s];++y)bp[s][y]=t[y],t[y]=0; 18 } 19 pre[s][0]=bp[s][0]; 20 for(int y=1;y<=bt[s];++y)pre[s][y]=(pre[s][y-1]+bp[s][y])%mod; 21 } 22 for(int s=1;s<=mst;++s)for(int x=0;x<=bt[mst^s];++x)if(dp[s][x]) 23 for(int u=s;u;u=u-1&s){ 24 dp[s^u][x]=(dp[s^u][x]+1ll*dp[s][x]*pre[u][min(bt[u],x)])%mod; 25 for(int y=x+1;y<=bt[u];++y)dp[s^u][y]=(dp[s^u][y]+1ll*dp[s][x]*bp[u][y])%mod; 26 } 27 }for(int i=1;i<=bt[mst];++i)ans=(ans+1ll*i*dp[0][i])%mod; cout<<ans<<endl; 28 }
T2:paint
大意:$w imes h$的矩阵中有$n$特殊点,要求选定一个周长最大的矩形使得其内部不含特殊点(不含边框)。$nle 10^5,w,h le 10^8$
发现任意一个$1 imes h$或$w imes 1$的矩形都一定合法。所以答案大于等于$(max(w,h)+1) imes 2$
所以说它一定越过了$x=frac{x}{2}$或$y=frac{h}{2}$。以前者为例,做扫描线,枚举矩形右边界。
用一个线段树维护每个点作为左端点的收益,即维护$max(-l+u-d)$。然后可以发现$u,d$的取值关于左端点单调,形式是单调栈。
所以就这么维护一下,线段树区间加全局$max$。要注意各种边界。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 611111 4 unordered_map<int,int>X; vector<int>Y[S]; 5 int n,w,h,ans,rx[S],x[S],y[S],u[S],ul[S],ut,d[S],dl[S],dt,v[S<<2],lz[S<<2],c,z,H; 6 #define lc p<<1 7 #define rc lc|1 8 #define md (L+R>>1) 9 void build(int p,int L,int R){lz[p]=0; 10 if(L==R){v[p]=-rx[L];return;} 11 build(lc,L,md); build(rc,md+1,R); v[p]=max(v[lc],v[rc]); 12 } 13 void add(int l,int r,int w,int p=1,int L=0,int R=c){if(r<l)return;//f(p==1)cerr<<l<<' '<<r<<' '<<w<<endl; 14 if(l<=L&&R<=r){v[p]+=w;lz[p]+=w;return;} 15 if(lz[p])v[lc]+=lz[p],v[rc]+=lz[p],lz[lc]+=lz[p],lz[rc]+=lz[p],lz[p]=0; 16 if(l<=md)add(l,r,w,lc,L,md); if(r>md)add(l,r,w,rc,md+1,R); v[p]=max(v[lc],v[rc]); 17 } 18 void solve(){ 19 memset(rx,0,sizeof rx); 20 H=h/2; n=z; X.clear(); 21 for(int i=1;i<=z;++i)rx[i]=x[i],x[++n]=x[i],y[n]=0,x[++n]=x[i],y[n]=h; 22 x[++n]=w;y[n]=0; x[++n]=0;y[n]=0; x[++n]=0;y[n]=h; x[++n]=w;y[n]=h; rx[n-1]=0; rx[n]=w; 23 sort(rx+1,rx+1+n); c=unique(rx+1,rx+1+n)-rx-1; 24 for(int i=1;i<=c;++i)X[rx[i]]=i; 25 for(int i=1;i<=n;++i)Y[X[x[i]]].push_back(y[i]); 26 build(1,0,c); 27 u[0]=H; d[0]=H+1; ut=dt=0; 28 for(int r=1;r<=c;++r){ 29 add(r-1,r-1,h);ans=max(ans,rx[r]+v[1]);add(r-1,r-1,-h); 30 for(int a:Y[r])if(a<=H){ 31 if(dl[dt]==r&&d[dt]>=a)continue; 32 while(d[dt]<=a)add(dl[dt-1],dl[dt]-1,d[dt]),dt--; 33 add(dl[dt],r-1,-a);d[++dt]=a;dl[dt]=r; 34 }else{ 35 if(ul[ut]==r&&u[ut]<=a)continue; 36 while(u[ut]>=a)add(ul[ut-1],ul[ut]-1,-u[ut]),ut--; 37 add(ul[ut],r-1,a);u[++ut]=a;ul[ut]=r; 38 }Y[r].clear(); 39 } 40 } 41 int main(){ 42 cin>>w>>h>>n; z=n; 43 for(int i=1;i<=z;++i)scanf("%d%d",&x[i],&y[i]); 44 solve(); for(int i=1;i<=z;++i)swap(x[i],y[i]); swap(h,w); solve(); 45 cout<<ans*2<<endl; 46 }
T3:Decompose
大意:将树剖分成若干长度$le L$的链,对于每条链,其中深度第$i$大的点$p$会贡献$w[p][i]$。最大化整棵树的贡献。
每次单点修改权值之后,进行回答。$n ,qL le 10^5,2 le L le 4$
看一眼就知道是$ddp$。一如既往的难写。由于$T2$调太久了就没时间写了。
首先暴力肯定就是$dp[p][i]=sumlimits_{u in son(p)} (maxlimits_{j=1}^{L} dp[u][j]) + maxlimits_{u in son(p)} (f[p][i-1] - (maxlimits_{j=1}^{L} dp[u][j]))$
然后那个$max$可以用$set$维护。暴跳父亲复杂度是$O(n^2logn)$的。
然后套个$ddp$的板子把上面这玩意轻重儿子分开弄一个矩阵,时间复杂度就变成$O(qlog^2nL^3+nL^3)$
按照$dp$定义这里的矩阵当然是$max-add$矩阵。
具体而言,考虑矩阵的构造,设$D[x]=egin{bmatrix} dp[x][1] \ dp[x][2] \ dp[x][3] \ dp[x][4] end{bmatrix}$。
我们需要构造转移矩阵$T[x]$。使得$D[x]=T[x]D[hson[x]]$
我们分情况讨论:对$dp[x][i]$产生贡献时,$i-1$是否来自$dp[hson[p]][i-1]$
首先我们考虑轻儿子如何产生贡献:按照$dp$定义,答案就是所有儿子的$maxdp[son]$再去掉其中最小的$dp[son][i-1]-maxdp[son]$
所以其中一部分就是$maxdp[son]$求和,这个可以直接拿数组维护,可以设$tot[i]$表示$i$的所有轻儿子的$maxdp$的和。每次修改轻儿子时直接减掉再加上新的就行。
另一部分就是$dp[son][i-1]-maxdp[son]$。这个可以用一个$multiset$来维护。用的时候取出最大的。修改的时候直接$erase$旧的$insert$新的。
所以可以设$maxc[i][j]$表示$i$的所有轻儿子的$dp[son][j-1]-maxdp[son]$的最小值。
于是上面这些东西我们都可以看成常量了。我们考虑一条重链,如何构造矩阵。
考虑矩阵的$(i,j)$元素。也就是说重儿子是链上第$j$个父亲是链上第$i$个。
那么,如果有$i=j+1$那么也就是说重儿子已经可以接在链下面了,不必再付出其它代价,转移系数也就是矩阵元素是$w[fa][i]+tot[fa]$
否则,就要从轻儿子里选择一个来做贡献,转移系数是$w[fa][i]+tot[fa]+maxc[fa][i]$
这样就可以了。
然后求一个点的$dp$值,其实就是查询从它到它所在重链的底端的点的矩阵的积,所得到的结果的第$L$列。
每次修改的时候,把当前点的$dp$值更新,然后跳重链,每次遇到一条轻边,就更新重链链顶的$dp$,删掉原来的贡献并加上新的。
树剖期望经过$log$次轻边。每次是一次线段树区间查询(只有一条重链),线段树单点修改(更新轻儿子贡献)。线段树上矩阵乘是$O(L^3logn)$的。
所以总复杂度就是这样的。常数写丑了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 111111 4 #define ll long long 5 #define inf 1000000000000000000 6 multiset<ll>s[S][5]; vector<int>to[S]; 7 int n,q,L,f[S],sz[S],hson[S],tp[S],tl[S],ET,dfn[S],tim,idfn[S],w[S][5]; 8 ll tot[S],dp[S][5],mx[S]; 9 struct matrix{ 10 ll a[5][5]; 11 matrix operator*(matrix y){ 12 matrix z; 13 for(int i=1;i<=L;++i)for(int j=1;j<=L;++j)z.a[i][j]=-inf; 14 for(int i=1;i<=L;++i)for(int j=1;j<=L;++j)for(int k=1;k<=L;++k) z.a[i][j]=max(z.a[i][j],a[i][k]+y.a[k][j]); 15 return z; 16 } 17 }v[S<<2],r; 18 void dfs(int p){ 19 sz[p]=1; mx[p]=-inf; 20 for(int i=2;i<=L;++i)s[p][i].insert(-inf); s[p][1].insert(0); 21 for(int i=0,y;y=i<to[p].size()?to[p][i]:0;++i){ 22 dfs(y); sz[p]+=sz[y]; tot[p]+=mx[y]; 23 if(sz[y]>sz[hson[p]])hson[p]=y; 24 for(int i=1;i<L;++i)s[p][i+1].insert(dp[y][i]-mx[y]); 25 }for(int i=1;i<=L;++i)dp[p][i]=tot[p]+*s[p][i].rbegin()+w[p][i],mx[p]=max(mx[p],dp[p][i]); 26 } 27 void DFS(int p,int top){ 28 tp[p]=top; tl[top]=p; dfn[p]=++tim; idfn[tim]=p; 29 if(hson[f[p]]==p){tot[f[p]]-=mx[p];for(int i=1;i<L;++i)s[f[p]][i+1].erase(s[f[p]][i+1].find(dp[p][i]-mx[p]));} 30 if(hson[p])DFS(hson[p],top); 31 for(int i=0,y;y=i<to[p].size()?to[p][i]:0;++i)if(y!=hson[p])DFS(y,y); 32 } 33 #define lc p<<1 34 #define rc lc|1 35 #define md (L+R>>1) 36 matrix ask(int l,int r,int p=1,int L=1,int R=n){ 37 if(l<=L&&R<=r)return v[p]; 38 if(l<=md&&r>md)return ask(l,r,lc,L,md)*ask(l,r,rc,md+1,R); 39 return l>md?ask(l,r,rc,md+1,R):ask(l,r,lc,L,md); 40 } 41 void chg(int P,int p=1,int L=1,int R=n){ 42 if(L==R){v[p]=r;return;} 43 if(P<=md)chg(P,lc,L,md);else chg(P,rc,md+1,R); v[p]=v[lc]*v[rc]; 44 } 45 void modify(int i){ 46 for(int j=1;j<=L;++j)for(int k=1;k<=L;++k)r.a[j][k]=w[i][j]+tot[i]+(j==k+1?0:*s[i][j].rbegin()); 47 chg(dfn[i]); 48 } 49 void get_dp(int p){ 50 r=ask(dfn[p],dfn[tl[p]]); mx[p]=-inf; 51 for(int i=1;i<=L;++i)dp[p][i]=r.a[i][L],mx[p]=max(mx[p],dp[p][i]); 52 } 53 int main(){ 54 cin>>n>>q>>L; 55 for(int i=2;i<=n;++i)scanf("%d",&f[i]),to[f[i]].push_back(i); 56 for(int i=1;i<=n;++i)for(int j=1;j<=L;++j)scanf("%d",&w[i][j]); 57 dfs(1); DFS(1,1); 58 for(int i=1;i<=n;++i)modify(i); 59 for(int i=1,p;i<=q;++i){ 60 scanf("%d",&p); for(int j=1;j<=L;++j)scanf("%d",&w[p][j]); 61 modify(p); p=tp[p]; 62 while(p!=1){ 63 for(int i=1;i<L;++i)s[f[p]][i+1].erase(s[f[p]][i+1].find(dp[p][i]-mx[p])); tot[f[p]]-=mx[p]; 64 get_dp(p); 65 for(int i=1;i<L;++i)s[f[p]][i+1].insert(dp[p][i]-mx[p]); tot[f[p]]+=mx[p]; 66 modify(f[p]); p=tp[f[p]]; 67 }get_dp(1); printf("%lld ",mx[1]); 68 } 69 }