[十二省联考2019]皮配(分类讨论的动态规划)
OI考场上正解爆零和不会有什么区别。 --鲁迅
可惜咱连正解都想不到
难得连着部分分加爆搜混了60分
结果
忘清一个数组
$60$->$10$
我枯了
讲解开始
①:有20分爆搜
②:首先考虑k=0的情况
经过简单推理发现:
每个城市选择阵营和每个学校选择派系互不干扰
对于选阵营和选派系各自做一个01背包dp
结果就是二者的合理方案数相乘
③:其次考虑有k的情况
除了“有下属学校有偏好的城市”和“有偏好的学校”以外
其他的学校/城市依然可以按照上面k=0的方法进行背包dp
它们和被偏好限制的城市/学校互不影响
此后考虑对k个偏好的处理
k只有30个,而与此同时...
$s_i$$leq$ $min$$($$10,M$$)$...
学校人数小于等于10!
这时方程已经出来了
dp[i][0/1][o][p]
对于前i个被限制的学校,该学校加入了0/1阵营,此时这些学校中加入了d0的有o人,这些学校所属的城市中加入了c0的有多少人
两种dp的结果的合理方案相乘的加和就是答案
时间复杂度 $O(c*M+n*M+k*(10*k)*M)$
详见代码(领略下精神就好哈)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using std::sort; 5 typedef long long lint; 6 const lint mo=998244353; 7 template<typename st> st max(st a,st b){return a>b?a:b;} 8 template<typename st> st min(st a,st b){return a<b?a:b;} 9 int T,n,c,c0,c1,d0,d1; 10 const int N=1011; 11 const int M=2511; 12 struct school 13 { 14 int cnt,ct,dis; 15 bool friend operator < (school a,school b) 16 { 17 if((a.dis!=-1)^(b.dis!=-1)) return(a.dis!=-1)>(b.dis!=-1); 18 return a.ct<b.ct; 19 } 20 }sc[N]; 21 struct city 22 { 23 int cnt,dis,dcnt; 24 }ct[N]; 25 int sum,sum2; 26 27 lint dp1[M],dp2[M]; 28 lint dp[33][2][331][M]; 29 30 31 int k; 32 33 void memclr() 34 { 35 memset(dp1,0,sizeof(dp1)); 36 memset(dp2,0,sizeof(dp2)); 37 dp1[0]=dp2[0]=1; 38 sum=0; 39 dp[0][0][0][0]=1; 40 sum2=0; 41 n=c0=c=c1=d0=d1=k=0; 42 } 43 44 void add(lint &vv,int u,int i,int o,int p) 45 { 46 if(o<0||p<0) return; 47 vv=(vv+dp[u][i][o][p])%mo; 48 } 49 lint fuck1(int l,int r) 50 { 51 return l>r?0:(l<=0?dp1[r]:(dp1[r]-dp1[l-1]+mo)%mo); 52 } 53 lint fuck2(int l,int r) 54 { 55 return l>r?0:(l<=0?dp2[r]:(dp2[r]-dp2[l-1]+mo)%mo); 56 } 57 58 int main() 59 { 60 scanf("%d",&T); 61 while(T--) 62 { 63 memclr(); 64 scanf("%d%d",&n,&c); 65 scanf("%d%d%d%d",&c0,&c1,&d0,&d1); 66 for(int i=0;i<=c;i++) ct[i].cnt=ct[i].dis=ct[i].dcnt=0; 67 for(int i=1;i<=n;i++) 68 { 69 scanf("%d%d",&sc[i].ct,&sc[i].cnt); 70 ct[sc[i].ct].cnt+=sc[i].cnt; 71 sum+=sc[i].cnt; 72 sc[i].dis=-1; 73 } 74 scanf("%d",&k); 75 for(int i=1,xin;i<=k;i++) 76 { 77 scanf("%d",&xin); 78 scanf("%d",&sc[xin].dis); 79 ct[sc[xin].ct].dis=1; 80 ct[sc[xin].ct].dcnt+=sc[xin].cnt; 81 sum2+=sc[xin].cnt; 82 } 83 sort(sc+1,sc+1+n); 84 for(int i=1;i<=n;i++)//对于没有偏好限制的学校的背包dp 85 { 86 if(sc[i].dis!=-1) continue; 87 for(int j=d0-sc[i].cnt;j>=0;j--) 88 { 89 dp1[j+sc[i].cnt]=(dp1[j+sc[i].cnt]+dp1[j])%mo; 90 } 91 } 92 for(int i=1;i<=c;i++)//对于没有偏好限制的城市的背包dp 93 { 94 if(ct[i].dis||ct[i].cnt==0) continue; 95 for(int j=c0-ct[i].cnt;j>=0;j--) 96 { 97 dp2[j+ct[i].cnt]=(dp2[j+ct[i].cnt]+dp2[j])%mo; 98 } 99 } 100 for(int i=1;i<=d0;i++) dp1[i]=(dp1[i]+dp1[i-1])%mo;//前缀和 101 for(int i=1;i<=c0;i++) dp2[i]=(dp2[i]+dp2[i-1])%mo;//前缀和 102 for(int i=1;i<=k;i++)//分类讨论对于k个有偏好学校的dp 103 { 104 for(int j=0;j<=1;j++) 105 { 106 for(int o=sum2;o>=0;o--) 107 { 108 for(int p=c0;p>=0;p--) 109 { 110 dp[i][j][o][p]=0; 111 switch(j) 112 { 113 case 0: 114 { 115 if(sc[i].dis!=0) 116 { 117 if(sc[i].ct==sc[i-1].ct) 118 { 119 add(dp[i][j][o][p],i-1,j,o-sc[i].cnt,p); 120 }else 121 { 122 add(dp[i][j][o][p],i-1,j,o-sc[i].cnt,p-ct[sc[i].ct].cnt); 123 add(dp[i][j][o][p],i-1,j^1,o-sc[i].cnt,p-ct[sc[i].ct].cnt); 124 } 125 } 126 if(sc[i].dis!=1) 127 { 128 if(sc[i].ct==sc[i-1].ct) 129 { 130 add(dp[i][j][o][p],i-1,j,o,p); 131 }else 132 { 133 add(dp[i][j][o][p],i-1,j,o,p-ct[sc[i].ct].cnt); 134 add(dp[i][j][o][p],i-1,j^1,o,p-ct[sc[i].ct].cnt); 135 } 136 } 137 break; 138 } 139 case 1: 140 { 141 if(sc[i].dis!=2) 142 { 143 if(sc[i].ct==sc[i-1].ct) 144 { 145 add(dp[i][j][o][p],i-1,j,o-sc[i].cnt,p); 146 }else 147 { 148 add(dp[i][j][o][p],i-1,j,o-sc[i].cnt,p); 149 add(dp[i][j][o][p],i-1,j^1,o-sc[i].cnt,p); 150 } 151 } 152 if(sc[i].dis!=3) 153 { 154 if(sc[i].ct==sc[i-1].ct) 155 { 156 add(dp[i][j][o][p],i-1,j,o,p); 157 }else 158 { 159 add(dp[i][j][o][p],i-1,j,o,p); 160 add(dp[i][j][o][p],i-1,j^1,o,p); 161 } 162 } 163 break; 164 } 165 } 166 } 167 } 168 } 169 } 170 int mic=sum-c1,mid=sum-d1; 171 if(mic>c0||mid>d0) 172 { 173 printf("0 "); 174 continue; 175 } 176 lint ans=0; 177 for(int i=0;i<=c0;i++) 178 { 179 for(int j=0;j<=sum2;j++) 180 { 181 lint vv=(dp[k][0][j][i]+dp[k][1][j][i])%mo; 182 if(vv) 183 { 184 ans=(ans+vv*((fuck1(mid-j,d0-j)*fuck2(mic-i,c0-i))%mo)%mo)%mo;//将合理方案相乘后加给答案 185 } 186 } 187 } 188 printf("%lld ",ans); 189 } 190 return 0; 191 }