[BZOJ 1004][HNOI2008]Cards
肉肉肉肉,,,
碰置换群啥的不是第一次了……这个题就是给你一堆置换(不要忘记还有一个幺元啊),然后限制颜色数量,求本质不同解个数。那么考虑使用Burnside引理,接下来考虑怎么计算每个置换的不动点数量,这个要求每个循环的颜色一致(不就事Polya定理了吗),所以说可以用背包DP搞一搞。
代码:
/**************************************************************
Problem: 1004
User: danihao123
Language: C++
Result: Accepted
Time:156 ms
Memory:3172 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <vector>
int sr, sb, sg, m, p;
int pow_mod(int a, int b) {
int ans = 1, res = a;
while(b) {
if(1 & b) ans = (ans * res) % p;
res = (res * res) % p; b >>= 1;
}
return ans;
}
int inv(int x) {
return pow_mod(x % p, p - 2);
}
int d[65][21][21][21];
std::vector<int> len;
int dp() {
int n = len.size();
d[0][0][0][0] = 1;
for(int i = 1; i <= n; i ++) {
int l = len[i - 1];
for(int j = 0; j <= sr; j ++) {
for(int k = 0; k <= sb; k ++) {
for(int t = 0; t <= sg; t ++) {
d[i][j][k][t] = 0;
if(j >= l) d[i][j][k][t] += d[i - 1][j - l][k][t];
if(k >= l) d[i][j][k][t] += d[i - 1][j][k - l][t];
if(t >= l) d[i][j][k][t] += d[i - 1][j][k][t - l];
d[i][j][k][t] %= p;
}
}
}
}
return d[n][sr][sb][sg];
}
int next[65]; bool vis[65];
int main() {
scanf("%d%d%d%d%d", &sr, &sb, &sg, &m, &p);
int n = sr + sb + sg;
int ans = 0;
for(int i = 1; i <= n; i ++) {
len.push_back(1);
}
ans = dp();
for(int i = 1; i <= m; i ++) {
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i ++) {
scanf("%d", &next[i]);
}
len.clear();
for(int i = 1; i <= n; i ++) {
if(!vis[i]) {
int p = i, cnt = 0;
do {
vis[p] = true; cnt ++;
p = next[p];
} while(p != i);
len.push_back(cnt);
}
}
ans = (ans + dp()) % p;
}
ans = (ans * inv(m + 1)) % p;
printf("%d\n", ans);
return 0;
}
[CodeVS 3729]飞扬的小鸟
论如何把一个完全背包出的高大上……
这个题啊……细节特别多……
首先,不建议开滚动数组,细节处理就会疯。
然后呢……真的不太好说了……还是看代码吧(尽管代码还是有明显的滚动数组痕迹)。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=10005,maxm=1005;
const int INF=0x7f7f7f7f;
#define REP(i,n) for(i=0;i<(n);i++)
#define REP_B(i,n) for(i=1;i<=(n);i++)
int GZ[maxn][2];
bool haveGZ[maxn];
int A[maxn][2];
int d[maxn][maxm];
int main(){
int n,m,k,temp;
register int i,j,v,now,pre,ans=INF,cnt;
scanf("%d%d%d",&n,&m,&k);
cnt=k;
REP(i,n){
scanf("%d%d",&A[i][0],&A[i][1]);
}
for(i=0;i<=n;i++){
GZ[i][0]=0;
GZ[i][1]=m+1;
}
REP_B(i,k){
scanf("%d",&temp);
haveGZ[temp]=true;
scanf("%d%d",&GZ[temp][0],&GZ[temp][1]);
}
memset(d,0x7f,sizeof(d));
memset(d[0],0,sizeof(d[0]));
d[0][0]=INF;
REP_B(i,n){
now=i;
pre=i-1;
d[now][0]=INF;
int& X=A[i-1][0];
int& Y=A[i-1][1];
REP_B(j,m){
if(j-X>0 && d[pre][j-X]<INF)
d[now][j]=min(d[now][j],d[pre][j-X]+1);
if(j-X>0 && d[now][j-X]<INF)
d[now][j]=min(d[now][j],d[now][j-X]+1);
if(j==m)
for(v=j-X;v<=m;v++){
if(d[pre][v]<INF)
d[now][j]=min(d[now][j],d[pre][v]+1);
if(d[now][v]<INF)
d[now][j]=min(d[now][j],d[now][v]+1);
}
}
for(j=GZ[i][0]+1;j<GZ[i][1];j++){
if(j+Y<=m && d[pre][j+Y]<INF)
d[now][j]=min(d[now][j],d[pre][j+Y]);
}
if(haveGZ[i]){
REP_B(j,GZ[i][0])
if(d[now][j]<INF){
d[now][j]=INF;
}
for(j=GZ[i][1];j<=m;j++)
if(d[now][j]<INF){
d[now][j]=INF;
}
}
}
for(i=n;i>=1;i--){
for(j=m;j>=1;j--){
ans=min(ans,d[i][j]);
}
if(ans<INF)
break;
if(haveGZ[i])
cnt--;
}
if(cnt==k){
printf("1\n%d\n",ans);
}else{
printf("0\n%d\n",cnt);
}
return 0;
}
[CodeVS 3269]混合背包
哎……现在才敢说真正会背包DP……
这个题可以分成三类问题分别处理,然后用一个一维数组一起递推。
0-1背包和完全背包都很简单。多重背包直接递推的话复杂度很高,可以考虑单调队列优化……然而窝太弱……不过还是用了
代码:
#include <cstdio>
#include <algorithm>
#include <queue>
#include <deque>
using namespace std;
const int maxn=201,maxv=200001;
struct DQueue{
deque<int> D;
queue<int> Q;
void push(int x){
Q.push(x);
while(!D.empty() && x>D.back())
D.pop_back();
D.push_back(x);
}
int top(){
return D.front();
}
void pop(){
if(D.front()==Q.front())
D.pop_front();
Q.pop();
}
int size(){
return Q.size();
}
void clear(){
while(!Q.empty())
Q.pop();
D.clear();
}
};
int d[maxv];
int v;
void pack(int V,int W,int c){
register int i,j,m;
if(c==1){
for(i=v;i>=V;i--)
d[i]=max(d[i],d[i-V]+W);
}else{
if(c==-1){
for(i=V;i<=v;i++)
d[i]=max(d[i],d[i-V]+W);
}else{
c=min(c,v/V);
for(i=0;i<V;i++){
m=(v-i)/V;
DQueue Q;
for(j=0;j<=m;j++){
if(Q.size()==c+1)
Q.pop();
Q.push(d[j*V+i]-j*W);
d[j*V+i]=Q.top()+j*W;
}
}
}
}
}
int main(){
register int i;
int n,x,y,z;
scanf("%d%d",&n,&v);
for(i=1;i<=n;i++){
scanf("%d%d%d",&x,&y,&z);
pack(x,y,z);
}
printf("%d\n",d[v]);
return 0;
}