这是一个不知道为什么反正就是想写成汇总的东西,还整合了之前写的一些DP题
因为沙茶博主DP很菜(什么都很菜,只是DP尤其菜)又做题少,所以写了这样一个记录沙茶博主在省选退役前刷的DP题的东西
好像上一行两句没有什么因果关系
-------------------废话结束---------------------------
前一阵的链接们:
虚树DP:SDOI 2011 消耗战 HNOI 2014 世界树
凸优化:八省联考2018 林克卡特树
决策单调性:NOI 2009 诗人小G
树形DP:CF1118F2 Tree Cutting 国家集训队 Crash的文明世界
乱七八糟的东西们:九省联考2018 CoaT(写的暴力) WC 2018 州区划分(并没搞太懂)
----------------------------------------------------------
转化
注意每个人的分数区间不可相交,然后莫得了
1 #include<map> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=100005,inf=1e9; 7 struct a 8 { 9 int ll,rr,val; 10 }mem[N]; 11 int n,t1,t2,cnt,ans,va[N],maxi[4*N]; 12 map<pair<int,int>,int> mp; 13 void Mini(int &x,int y) 14 { 15 if(x>y) x=y; 16 } 17 bool cmp(a x,a y) 18 { 19 return x.ll==y.ll?x.rr<y.rr:x.ll<y.ll; 20 } 21 void Count(int l,int r) 22 { 23 pair<int,int> pr=make_pair(l,r); 24 if(!mp.count(pr)) mp[pr]=++cnt; 25 int id=mp[pr]; va[id]++; 26 mem[id]=(a){l,r,va[id]}; 27 } 28 void Change(int nde,int l,int r,int pos,int tsk) 29 { 30 if(l==r) 31 maxi[nde]=tsk; 32 else 33 { 34 int mid=(l+r)>>1,ls=2*nde,rs=2*nde+1; 35 if(pos<=mid) Change(ls,l,mid,pos,tsk); 36 else Change(rs,mid+1,r,pos,tsk); 37 maxi[nde]=max(maxi[ls],maxi[rs]); 38 } 39 } 40 int Query(int nde,int l,int r,int ll,int rr) 41 { 42 if(l>rr||r<ll) 43 return -inf; 44 else if(l>=ll&&r<=rr) 45 return maxi[nde]; 46 else 47 { 48 int mid=(l+r)>>1,ls=2*nde,rs=2*nde+1; 49 return max(Query(ls,l,mid,ll,rr),Query(rs,mid+1,r,ll,rr)); 50 } 51 } 52 int main() 53 { 54 scanf("%d",&n); 55 for(int i=1;i<=n;i++) 56 { 57 scanf("%d%d",&t1,&t2); 58 if(t2+1>n-t1) continue; 59 Count(t2+1,n-t1); 60 } 61 for(int i=1;i<=cnt;i++) 62 Mini(mem[i].val,mem[i].rr-mem[i].ll+1); 63 sort(mem+1,mem+1+cnt,cmp); 64 // for(int i=1;i<=cnt;i++) printf("%d %d %d\n",mem[i].ll,mem[i].rr,mem[i].val); 65 for(int i=1;i<=cnt;i++) 66 { 67 int qry=Query(1,0,n,0,mem[i].ll-1); 68 int newa=qry+mem[i].val; 69 Change(1,0,n,mem[i].rr,newa),ans=max(ans,newa); 70 } 71 printf("%d ",n-ans); 72 return 0; 73 }
转化
互质归根结底是没有相同质因数,而每个数只可能有一个大于$\sqrt n$的质因数。搞出来每个数小于$\sqrt n$的质因数的状态和(是否有)大于$\sqrt n$的质因数,按后者从小到大排序,大于$\sqrt n$的质因数相同的一块DP(没有的每个单独DP)。然后基本莫得了,注意容斥掉两个人都没选的情况
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=510,M=260,all=255; 6 const int pri[8]={2,3,5,7,11,13,17,19}; 7 long long n,mod,cnt,ans,dp[M][M],fir[M][M],sec[M][M]; 8 struct a 9 { 10 int sta,hug; 11 }num[N]; 12 bool cmp(a x,a y) 13 { 14 return x.hug==y.hug?x.sta<y.sta:x.hug<y.hug; 15 } 16 void Add(long long &x,long long y) 17 { 18 x+=y; 19 if(x>=mod) x-=mod; 20 } 21 void Pre() 22 { 23 for(int i=2;i<=n;i++) 24 { 25 int tmp=i; 26 for(int j=0;j<=7;j++) 27 if(tmp%pri[j]==0) 28 { 29 num[i].sta|=1<<j; 30 while(tmp%pri[j]==0) tmp/=pri[j]; 31 } 32 num[i].hug=tmp; 33 } 34 sort(num+2,num+1+n,cmp),dp[0][0]=1; 35 } 36 int main() 37 { 38 scanf("%lld%lld",&n,&mod),Pre(); 39 for(int i=2;i<=n;i++) 40 { 41 if(i==2||num[i].hug==1||num[i].hug!=num[i-1].hug) 42 { 43 for(int j=0;j<=all;j++) 44 for(int k=0;k<=all;k++) 45 fir[j][k]=sec[j][k]=dp[j][k]; 46 } 47 for(int j=all;~j;j--) 48 for(int k=all;~k;k--) 49 if(!(j&k)) 50 { 51 if(!(k&num[i].sta)) Add(fir[j|num[i].sta][k],fir[j][k]); 52 if(!(j&num[i].sta)) Add(sec[j][k|num[i].sta],sec[j][k]); 53 } 54 if(i==n||num[i].hug==1||num[i].hug!=num[i+1].hug) 55 { 56 for(int j=all;~j;j--) 57 for(int k=all;~k;k--) 58 if(!(j&k)) dp[j][k]=(fir[j][k]+sec[j][k]-dp[j][k]+mod)%mod; 59 } 60 } 61 for(int i=0;i<=all;i++) 62 for(int j=0;j<=all;j++) 63 if(!(i&j)) Add(ans,dp[i][j]); 64 printf("%lld",ans); 65 return 0; 66 }
分析
直接做看起来不可做,分析性质,发现每次一定是拔一个后缀。因为后面比它高的拔了不会使答案变劣,后面不比它高的拔了还是没贡献,最后还拔走就行。
于是可以设计一个DP:$dp[i][j]$