[考试反思]0427省选模拟81:浪费
考这场考试不如补觉。
怀疑组题人(都不算是出题人)就是从网上随便摘了三道题揉巴揉巴题解也懒得写了就直接丢上来了。
连部分分都瞎出完了就
$4.5$小时。面对两个视频课原题但是根本没法写。然后面对出题人口胡根本没讲
做这套题就纯当看了下$IOI$的新闻。。。。
T1:拿钱了
暂时无解。
T2:打铁了
大意:$n$人走两条路,你预先知道每个人的走完所需要的时间$t_i$。然后人依次来你可以安排他走哪条路。
每个人会产生(含他在他之前走同一条路的最大$t_j$)$-t_i$的代价。最小化代价和。$n \le 2\ times 10^5$
不会。去看牛的博客。然而大神都不写博客
$dy$讲的原题,他讲的暴力是对的,剩下乱糟糟的。
发$std$了而且牛神$AC$了,有空再问吧。
然而直接颓$std$看明白了就$AC$了。很快乐。
设$dp_{i,j}$表示已经考虑前$i$个人,其中前缀最大值不在的那条路的最大值是$j$,此情况下的最小代价。
这就可以$O(n^2)$做了。
($[1,a_i)$区间加常数即前缀最大值,$[a_i,mx]$区间加下标,$[1,a_i)$前缀最小值$+a_i$与$a_i$单点取$min$)
然后有几个显而易见的事实:首先$dp$数组随着$j$增长而单调不增。
其次,只有以前出现过的$a_i$才可能作为最小值决策点出现。
所以我们用一个$set$维护可能的决策点。然后搞一个线段树维护这些东西:
区间加常数懒标记,区间加下标懒标记
设$t_i$表示决策点$i$经过多少轮后会比后继决策点更差。线段树维护它的最小值。
现在上述操作就都没有问题了。只不过发现全局$t\le 0$时需要线段树二分去找到没用的决策点把它删掉,并更新前驱后继。
单点修改的时候也要考虑这一点。
只不过是代码细节有点多。$O(nlogn)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define lc p<<1 4 #define rc lc|1 5 #define md (L+R>>1) 6 #define S 800005ll 7 #define ll long long 8 int t[S],n,c,a[S],r[S],k[S]; ll b[S],v[S],tot; 9 set<int>s; 10 void up(int p){t[p]=min(t[lc],t[rc]);} 11 void down(int p){k[lc]+=k[p];k[rc]+=k[p];b[lc]+=b[p];b[rc]+=b[p];t[lc]-=k[p];t[rc]-=k[p];k[p]=b[p]=0;} 12 void build(int p,int L,int R){ 13 if(L==R){v[p]=L?(L<c?0:-1):1e18;t[p]=S;return;} 14 build(lc,L,md);build(rc,md+1,R); up(p); 15 } 16 void chgt(int x,int z,int p=1,int L=0,int R=c){ 17 if(L==R){t[p]=z;return;} 18 down(p); x<=md?chgt(x,z,lc,L,md):chgt(x,z,rc,md+1,R); up(p); 19 } 20 ll ask(int x,int p=1,int L=0,int R=c){ 21 if(L==R){v[p]+=b[p]+1ll*k[p]*r[L];k[p]=b[p]=0;return v[p];} 22 down(p); return x<=md?ask(x,lc,L,md):ask(x,rc,md+1,R); 23 } 24 void chgv(int x,ll z,int p=1,int L=0,int R=c){ 25 if(L==R){ 26 v[p]+=b[p]+1ll*k[p]*r[L];k[p]=b[p]=0; 27 v[p]=min(v[p],z);s.insert(x); 28 int pre=*(--s.lower_bound(x)),suc=*s.upper_bound(x); ll t; 29 if(pre)t=ask(pre),chgt(pre,t<=z?0:min((t-z-1)/(r[x]-r[pre])+1,S)); 30 if(suc!=c)t=ask(suc),chgt(x,z<=t?0:min((z-t-1)/(r[suc]-r[x])+1,S)); 31 return; 32 }down(p); x<=md?chgv(x,z,lc,L,md):chgv(x,z,rc,md+1,R); up(p); 33 } 34 void addk(int l,int r,int p=1,int L=0,int R=c){ 35 if(l<=L&&R<=r){t[p]--;k[p]++;return;} 36 down(p); if(l<=md)addk(l,r,lc,L,md); if(r>md)addk(l,r,rc,md+1,R); up(p); 37 } 38 void addb(int l,int r,int z,int p=1,int L=0,int R=c){ 39 if(l<=L&&R<=r){b[p]+=z;return;} 40 if(l<=md)addb(l,r,z,lc,L,md); if(r>md)addb(l,r,z,rc,md+1,R); 41 } 42 ll Ask(int x){return ask(*(--s.upper_bound(x)));} 43 int find(int p=1,int L=0,int R=c){if(L==R)return L;down(p);return t[lc]<=0?find(lc,L,md):find(rc,md+1,R);} 44 int main(){ 45 cin>>n; for(int i=1;i<=n;++i)scanf("%d",&a[i]),tot-=r[i]=a[i]; 46 sort(r+1,r+1+n); c=unique(r+1,r+1+n)-r; 47 s.insert(0);s.insert(1);s.insert(c); build(1,0,c); 48 for(int i=1,mx=0;i<=n;++i){ 49 mx=max(mx,a[i]); 50 if(a[i]==mx){tot+=mx;continue;} 51 int x=lower_bound(r+1,r+c,a[i])-r; ll mn=Ask(x); 52 if(x>1)addb(1,x-1,mx); addk(x,c-1); chgv(x,mn+a[i]); 53 while(t[1]<=0){ 54 x=*s.upper_bound(find()); int pre=*(--s.lower_bound(x)),suc=*s.upper_bound(x); 55 ll v1=ask(pre),v2=ask(suc); 56 if(suc!=c)chgt(pre,v1<=v2?0:min((v1-v2-1)/(r[suc]-r[pre])+1,S)); 57 else chgt(pre,S); 58 chgt(x,S);s.erase(x); 59 } 60 }cout<<tot+Ask(c-1)<<endl; 61 }
T3:梦里啥都有
原题《青春猪头少年不会梦到兔女郎学姐》。数据范围削弱$n \le 50,a_i \le 100$
没时间写了。。
https://www.luogu.com.cn/blog/user13052/solution-p6151
然而这篇博客有些地方写的不是很好。
首先是关于所有方案除$j$再乘$m$。这是对的但是他解释的有问题。
考虑到有些方案中是有循环节的,再这时候不能单纯考虑被计算了几次而直接除再乘,因为直接乘会出现同构的串。
一种正确的理解方式是:对于一个已知串,它本应该被计算的次数是 循环节长度 次。
然而事实上直接做的话统计到的次数是第一段长度乘以循环节个数除以总长度。所以才需要一乘一除。
另一方面它说的不好的一点是,具体实现过程中,并不需要用一端的减去两端的。
具体而言这么考虑,对于一种首长度为$4$尾长度为$3$的方案,为什么不用再刻意减掉它?
因为你做长度为$7$的首端的时候带上了容斥系数把对应的干掉了,同时你还乘了$m$做轮换,所以$4-3$这种轮换也是被容斥掉了。
时间复杂度和代码倒好说。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 10001 4 #define mod 1000000007 5 int n,r[S],tot,fac[S],inv[S],F[55][105],dp[S],R[S]; 6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 7 int C(int b,int t){return t>b||t<0?0:fac[b]*1ll*inv[t]%mod*inv[b-t]%mod;} 8 int cal(int _){ 9 for(int i=_;i<=r[1];++i)dp[i]=1ll*F[1][i]*inv[i-_]%mod*inv[i]%mod*fac[i-1]%mod; 10 int len=r[1]; 11 for(int i=2;i<=n;++i){ 12 for(int j=1;j<=len;++j)for(int k=1;k<=r[i];++k)R[j+k]=(R[j+k]+1ll*dp[j]*F[i][k]%mod*inv[k])%mod; 13 len+=r[i]; 14 for(int j=1;j<=len;++j)dp[j]=R[j],R[j]=0; 15 }int ans=0; 16 for(int i=_;i<=len;++i)ans=(ans+1ll*dp[i]*fac[i-_])%mod; 17 return ans*1ll*tot%mod; 18 } 19 int main(){ 20 cin>>n; for(int i=1;i<=n;++i)cin>>r[i],tot+=r[i]; 21 for(int i=fac[0]=1;i<S;++i)fac[i]=fac[i-1]*1ll*i%mod; 22 inv[S-1]=qp(fac[S-1],mod-2); 23 for(int i=S-2;~i;--i)inv[i]=inv[i+1]*(i+1ll)%mod; 24 for(int i=1;i<=n;++i)for(int j=1;j<=r[i];++j)for(int a=j;a<=r[i];++a) 25 F[i][j]=(F[i][j]+1ll*C(r[i]+a-1,a+a-1)*(a-j&1?mod-1:1)%mod*C(a-1,j-1))%mod; 26 cout<<cal(1)<<endl; 27 }