[洛谷P3600]隨機數生成器
我真是傻逼,這道題做了兩個晚上還沒做出來,巫蠱偶大佬看了一眼就秒掉了,後來還是在巫蠱偶神仙的提示下做出來的……只能說明我太菜了。
首先我們可以發現如果一段區間包含了另外一段區間,那麼大的區間是沒有用的。所以我們去掉所有沒有用的區間之後把這些區間按左端點排序之後右端點一定也是單調遞增的。這些區間大概可能是張這樣的:

然後因為期望不能做極值問題所以我們肯定是要列舉這個最小值的最大值的。
我們發現形如答案是某個值的方案數非常難求,所以我們可以把它變為形如答案小於等於某個值的方案數,最後處理一下就好了。然後考慮使用DP來解。
首先列舉這個值k,一個很顯然的DP是$f(i,j)$表示到第i個位置,正好前面j個區間的最小值已經小於等於k的方案數,這是一個2d/0d動態規劃,我們改變一下狀態就可以把他變為一個1d/1d動態規劃。$f(i)$表示正好前i段區間的區間最小值已經小於等於k的方案數,然後預處理一下逆元,用一些數學式子轉移一下就好了。然後我們發現這個貌似可以用一個類似於字首和的東西優化,所以就沒了,時間複雜度$O(n^2)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAX_N=2005,MOD=666623333; int fpow(int x,int n){ int ret=1; for(;n;n>>=1,x=(ll)x*x%MOD) n&1?ret=(ll)ret*x%MOD:0; return ret; } int f[MAX_N][MAX_N],c[MAX_N],b[MAX_N],p[MAX_N][MAX_N],inv[MAX_N][MAX_N]; pair<int,int> a[MAX_N]; int main(){ int n,x,q; scanf("%d%d%d",&n,&x,&q); for(int i=1;i<=q;++i) scanf("%d%d",&a[i].first,&a[i].second); for(int i=1;i<=q;++i) for(int j=1;j<=q;++j) if(i!=j&&a[j].first!=-1&&a[i].first<=a[j].first &&a[i].second>=a[j].second){ a[i].first=-1; break; } int top=0; for(int i=1;i<=q;++i) if(a[i].first!=-1) a[++top]=a[i]; sort(a+1,a+top+1); q=top; for(int i=1;i<=x;++i){ p[i][0]=1,p[i][1]=(ll)i*fpow(x,MOD-2)%MOD; inv[i][0]=1,inv[i][1]=fpow(p[i][1],MOD-2); for(int j=2;j<=n+2;++j){ p[i][j]=(ll)p[i][j-1]*p[i][1]%MOD; inv[i][j]=(ll)inv[i][j-1]*inv[i][1]%MOD; } } for(int i=1;i<=q;++i) b[a[i].first]++,c[a[i].second+1]++; for(int t=1;t<x;++t){ f[t][0]=1; int poi=0,cnt=0,key=0; for(int i=1;i<=n;++i){ if(b[i]){ key=((ll)f[t][poi]*p[x-t][n-a[poi+1].first]+key)%MOD; ++cnt,++poi; } if(c[i]){ key=(key+MOD-(ll)f[t][poi-cnt] *p[x-t][n-a[poi-cnt+1].first]%MOD)%MOD; --cnt; } f[t][poi]=(f[t][poi]+(ll)key*inv[x-t][n-i]%MOD*p[t][1])%MOD; } } f[x][q]=1; int ans=0; for(int i=1;i<=x;++i){ ans=((ll)i*(f[i][q]+MOD-f[i-1][q])+ans)%MOD; } printf("%d",ans); return 0; }