題解:
比較明顯的期望DP
設f[n]表示 n 變成1的期望步數
則f[n]=1+\frac{\sum_{d|n}f[d]}{d(n)} (d(n)表示n的因子個數)
移一下項\frac{(d(n)-1)f[n]}{d(n)}=1+\frac{\sum_{d|n,d<n}f[d]}{d(n)}
f[n]=\frac{d(n)+\sum_{d|n,d<n}f[d]}{d(n)-1}
我們發現這個轉移其實只與n的所有質因子的次冪的可重集有關
根據一個結論,我們知道了在n<=10^24是,本質不同的可重集個數爲170000+
我們可以爆搜出所有的可重集,然後進行DP即可
代碼:(第一次寫雙longlong壓12位,感覺挺不錯的)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
#define N 30
#define LL unsigned long long
char s[N+5];
const LL pw=1000000000000ull;
struct big{
LL a[2];
big(){a[0]=a[1]=0;}
big(int x){a[0]=x;a[1]=0;}
big operator *= (const int x){
a[0]*=x;a[1]*=x;
if(a[0]>=pw)a[1]+=a[0]/pw,a[0]%=pw;
return *this;
}
bool operator < (const big &t)const{
return a[1]<t.a[1]||(a[1]==t.a[1]&&a[0]<t.a[0]);
}
int operator %(const int t)const{
return (pw*(a[1]%t)+a[0]%t)%t;
}
big operator /= (const int x){
a[0]+=pw*(a[1]%x);
a[1]/=x;a[0]/=x;
return *this;
}
void read(){
if(!~scanf("%s",s))return;
int i,j,len=strlen(s);a[0]=a[1]=0;
for(i=0;i*12<len;i++)
for(j=min(12,len-i*12);j>=1;j--)
a[i]=a[i]*10-48+s[len-i*12-j];
}
}n,lim;
LL gethh(vector<int> a)
{
sort(a.begin(),a.end());
LL hh=0;
for(int i=0;i<int(a.size());i++)
hh=(137ull*hh+a[i]);
return hh;
}
int prime[N]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71};
vector<int> now,zer;
map<LL,int> id;
pair<big,vector<int> > tmp[200005];
int tcnt,ans[200005],sum[200005][21];
void dfs(big x,int k,int pre,vector<int> e)
{
//printf("%012llu%012llu\n",x.a[1],x.a[0]);
tmp[++tcnt]=make_pair(x,e);
e.push_back(0);
for(int i=1;i<=pre&&(x*=prime[k])<lim;i++)
e.back()++,dfs(x,k+1,i,e);
}
const int mod=1000000007;
int ksm(int x,int y)
{
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
y>>=1;x=1ll*x*x%mod;
}
return ret;
}
int main()
{
freopen("div.in","r",stdin);
freopen("div.out","w",stdout);
int m,i,j,x;
lim.a[1]=pw;
lim.a[0]=1;
dfs(big(1),1,80,zer);
for(i=1;i<=tcnt;i++)id[gethh(tmp[i].second)]=i;
for(i=2;i<=tcnt;i++){
for(j=tmp[i].second.size()-1;j>=0;j--){
sum[i][j]=sum[i][j+1];
if(tmp[i].second[j]){
vector<int> tt=tmp[i].second;
tt[j]--;
sum[i][j]=(sum[i][j]+sum[id[gethh(tt)]][j])%mod;
}
}
int d=1;
for(j=tmp[i].second.size()-1;j>=0;j--)
d*=tmp[i].second[j]+1;
ans[i]=1ll*(d+sum[i][0])*ksm(d-1,mod-2)%mod;
for(j=0;j<=19;j++)
sum[i][j]=(sum[i][j]+ans[i])%mod;
}
//printf("%d\n",tcnt);
while(n.read(),~scanf("%d",&m)){
vector<int>a;
for(i=1;i<=m;i++){
scanf("%d",&x);
int cnt=0;
while(n%x==0)n/=x,cnt++;
a.push_back(cnt);
}
printf("%d\n",ans[id[gethh(a)]]);
}
}
題解:噁心分類討論題
其實各種分類討論都是可以過的
簡單講講我自己的分類討論
首先把含有###或##.後面的字符串斷開
然後按照##*來進行分段
考慮每一個段,如果當前的干擾器個數大於等於2,就一定可以把這一段的干擾器全部拿完
否則就考慮一下幾種情況:(假設此時的干擾器個數爲0)
1、*..#...*..#
2、#*....#..*
3、#*...*..#
4、*..#....#*
5、*..#....#..*
6、*..#....#*.....#...#*
我們設 *#. 的情況爲flg1, .#* 的情況爲flg2
那麼出現11、22、21的情況則可以拿到2個干擾器,進而拿完所有的干擾器
而出現12的情況(4)則只能拿一個,而出現情況(5)則無法通過此段
情況(6)就是情況(4)的嵌套
注意一下判斷就行了
代碼:(考試的時候少判了“if(flg1){now=2;return 1;}”以及開小了數組,100->20。。。難受)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 3000005
char a[N];
int cnt,pos[N],sum[N],ans;
bool solve(int l,int r,int &now)
{
if(now>=2)return 1;
bool flg1=0,flg2=0;
for(int i=l;i<=r;i++){
if(a[i]=='*'){
now++,ans=max(ans,now);
if(flg1){now=2;return 1;}
}
if(now>=2)return 1;
if(a[i]=='#')continue;
if(i<r-1&&a[i+1]=='#'&&a[i]=='*'&&a[i+2]=='*'){now=2;return 1;}
if(i<r-1&&a[i+1]=='#'&&a[i]=='*'&&a[i+2]=='.'){
if(flg1||flg2){now=2;return 1;}
if(!flg1)flg1=1,now--;
}
if(i<r-1&&a[i+1]=='#'&&a[i]=='.'&&a[i+2]=='*'){
if(!flg2)flg2=1;
else{now=2;return 1;}
flg1=0;
}
if(i<r-1&&a[i+1]=='#'&&a[i]=='.'&&a[i+2]=='.'){
if(!now)return 0;
else now--,flg1=1,flg2=0;
}
}
return 1;
}
int main()
{
freopen("tower.in","r",stdin);
freopen("tower.out","w",stdout);
int T,n,i;
scanf("%d",&T);
while(T--){
scanf("%s",a+1);
n=strlen(a+1);cnt=0;
a[n+1]=a[n+2]='#';
for(i=1;i<=n;i++){
sum[i]=sum[i-1];
if(a[i]=='*')sum[i]++;
if(a[i]=='#'&&a[i+1]=='#')pos[++cnt]=i+1;
if(a[i]=='#'&&a[i+1]=='#'&&(a[i+2]=='#'||a[i+2]=='.')){n=i-1;break;}
}
pos[++cnt]=n+2;
int now=0;ans=0;
for(i=1;i<=cnt;i++){
bool flg=solve(pos[i-1]+1,pos[i]-2,now);
if(now>=2)now=sum[pos[i]-2]-(i-1),ans=max(ans,now);
if(!flg)break;
if(i<cnt){
if(now)now--;
else break;
}
}
printf("%d\n",ans);
}
//1 ...*.#.*..#..*
}
/*
1
..*..#....*.#..*..#
*/
題解:利用取值分界點的單調性+單調隊列來優化DP
首先發現空位只填-1或K是最優的
再發現真實答案一定是可以取完整個區間[1,n]的(如果a1或an爲負數則向內縮進一步)
於是我們設f[j][i]表示在[1,i]中放了j個-1的小b的答案的最小值
那麼我們真實答案就是sum[n]-j*(K+1)-f[j][n](sum[n]表示1~n所有空位都填K的前綴和)
則有f[j][i]=min_{1~i}(max(f[j-1][k-1],sum[i]-sum[k]))
注意整個DP是主動放置-1來分段,而且我們的決策點只能是在偶數位上,如果奇數位上有負數,這個DP就會被強制分段
這就是O(n^3)的做法
考慮優化
我們要求的是兩個值的max的最小值
我們猜測存在一個分界點o
使得k∈[1,o]時,max(f[j-1][k-1],sum[i]-sum[k])=sum[i]-sum[k]
k∈[o,i)時,max(f[j-1][k-1],sum[i]-sum[k])=f[j-1][k-1]
其實這也是顯然的,因爲sum[i]-sum[k]是關於k單調遞減的,f[j-1][k-1]是關於k單調遞增的(因爲如果放置的-1數目不變,越到後面,小b的最大子段和就會越大)
我們再大膽猜測o是關於i單調遞增的
因爲若k∈[1,o],則要滿足條件sum[i]-sum[k]>=f[j-1][k-1]
移一下項sum[i]>=f[j-1][k-1]+sum[k]
由於sum[i]是遞增的,sum[k]是遞增的,f[j-1][k-1]也是遞增的
所以當i=i+1之後,會有更多的k屬於[1,o]這個區間
現在我們利用單調性維護出了分段點o
那麼前半段的貢獻可以通過維護sum[k]的前綴最大值來完成(現在的目的是求sum[i]-sum[k]的最小值)
後半段的貢獻可以通過單調隊列來維護出f[j-1][k-1]的最小值
代碼:(理論上來說可以直接維護分界點o來轉移的,但是過不了對拍。。。)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
#define LL long long
const int INF=0x3f3f3f3f;
int a[N],q[N],he,ta;
int f[5005][N],sum[N];
int main()
{
freopen("subsegment.in","r",stdin);
freopen("subsegment.out","w",stdout);
int n,K,i,j,k;
scanf("%d%d",&n,&K);
for(i=1;i<=n;i++)
scanf("%d",&a[2*i-1]);
n=2*n-1;
a[1]=max(a[1],0);a[n]=max(a[n],0);
for(i=1;i<=n;i++)sum[i]=sum[i-1]+(i&1?a[i]:K);
memset(f,0x3f,sizeof(f));
for(i=1,k=0;i<=n;i++){
if(a[i]<0)k=i;
f[0][i]=max(k?f[0][k-1]:0,sum[i]-sum[k]);
}
int ans=sum[n]-f[0][n],o,mx;
for(j=1;j<=(n>>1);j++){
ta=o=0,he=1,mx=-INF;
for(i=1,k=0;i<=n;i++){
if(a[i]<0)k=i,o=i-1,ta=0,he=1,mx=-INF;
if(k<=i)f[j][i]=max(k?f[j][k-1]:0,sum[i]-sum[k]);
if(k==i)continue;
while(o+2<=i&&f[j-1][o+2-1]+sum[o+2]<=sum[i])
o+=2,mx=max(mx,sum[o]);
while(he<=ta&&o>=q[he])he++;
f[j][i]=min(f[j][i],sum[i]-mx);
if(he<=ta)f[j][i]=min(f[j][i],f[j-1][q[he]-1]);
if(!(i&1)){
while(he<=ta&&f[j-1][i-1]<=f[j-1][q[ta]-1])
ta--;
q[++ta]=i;
}
}
ans=max(ans,sum[n]-j*(K+1)-f[j][n]);
}
printf("%d\n",ans);
}