题解:
设状态\(F[S]\),分四个阶段转移:
1.\(F[S]\)表示可以组成集合\(S\)的有多少盒子
2.\(F[S]\)表示可以组成\(S\)的子集的有多少盒子
3.\(F[S]\)表示并集为\(S\)的方案数
4.\(F[S]\)表示有多少种方式可以表示\(S\)的子集
答案即第三阶段时的\(F[\)全集\(]\)
\(code\):
#include<stdio.h>
#include<ctype.h>
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc()
{
// return getchar();
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
inline void read(T &x)
{
char tt;
while(!isdigit(tt=gc()));x=tt-'0';
while(isdigit(tt=gc())) x=x*10+tt-'0';
}
int n,m,tot;
int f[1<<23],pow_[1<<23];
const int mod=1e9+7;
inline int add(int a,int b){return a+b<mod?a+b:a+b-mod;}
inline int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
int main()
{
read(n),read(m);
tot=1<<m;
for(int i=1;i<=n;i++)
{
int t,x,s=0;read(t);
for(int j=1;j<=t;j++)
read(x),s|=1<<(x-1);f[s]++;
}
for(int i=0;i<m;i++)
for(int s=0;s<tot;s++)
if(s>>i&1)
f[s]=add(f[s],f[s^(1<<i)]);
pow_[0]=1;
for(int i=1;i<=(1<<22);i++)
pow_[i]=add(pow_[i-1],pow_[i-1]);
for(int s=0;s<tot;s++)
f[s]=pow_[f[s]];
for(int i=0;i<m;i++)
for(int s=0;s<tot;s++)
if(s>>i&1)
f[s]=sub(f[s],f[s^(1<<i)]);
printf("%d",f[tot-1]);
}
题解:(暂未做,copy出题人原话)
一个状态应该包含,每种颜色的剩余数量,以及当前画的最后一笔的颜色。
所以状态就是\(f[k][(i_1...,i_n)]\)表示最后一笔颜色为k,各颜色还剩.... in支的时候,颜色段数的期望值。
这样并不能递推,还缺一个辅助的状态\(p[k][(i_1,...i_n)]\), 表示到达当前状态的概率。
\((std)code:\)
#include <cstdio>
#include <cstdlib>
using namespace std;
int N,A,a[10];
double f[5][16][16][16][16][16],p[5][16][16][16][16][16];
int main()
{
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
int i,j,k,i0,i1,i2,i3,i4;
double tf,tp;
scanf("%d",&N);
for(i=0;i<N;i++)
{
scanf("%d",&a[i]);
A+=a[i];
}
if(a[0]>0)
f[0][a[0]-1][ a[1] ][ a[2] ][ a[3] ][ a[4] ]=
p[0][a[0]-1][ a[1] ][ a[2] ][ a[3] ][ a[4] ]=1.0*a[0]/A;
if(a[1]>0)
f[1][ a[0] ][a[1]-1][ a[2] ][ a[3] ][ a[4] ]=
p[1][ a[0] ][a[1]-1][ a[2] ][ a[3] ][ a[4] ]=1.0*a[1]/A;
if(a[2]>0)
f[2][ a[0] ][ a[1] ][a[2]-1][ a[3] ][ a[4] ]=
p[2][ a[0] ][ a[1] ][a[2]-1][ a[3] ][ a[4] ]=1.0*a[2]/A;
if(a[3]>0)
f[3][ a[0] ][ a[1] ][ a[2] ][a[3]-1][ a[4] ]=
p[3][ a[0] ][ a[1] ][ a[2] ][a[3]-1][ a[4] ]=1.0*a[3]/A;
if(a[4]>0)
f[4][ a[0] ][ a[1] ][ a[2] ][ a[3] ][a[4]-1]=
p[4][ a[0] ][ a[1] ][ a[2] ][ a[3] ][a[4]-1]=1.0*a[4]/A;
for(j=A-1;j>=1;j--)
for(k=0;k<=4;k++)
for(i0=j-a[1]-a[2]-a[3]-a[4],i0=i0<0?0:i0;i0<=a[0]-(k==0)&&i0 <=j;i0++)
for(i1=j- i0 -a[2]-a[3]-a[4],i1=i1<0?0:i1;i1<=a[1]-(k==1)&&i0+i1 <=j;i1++)
for(i2=j- i0 - i1 -a[3]-a[4],i2=i2<0?0:i2;i2<=a[2]-(k==2)&&i0+i1+i2 <=j;i2++)
for(i3=j- i0 - i1 - i2 -a[4],i3=i3<0?0:i3;i3<=a[3]-(k==3)&&i0+i1+i2+i3<=j;i3++)
{
i4=j-i0-i1-i2-i3;
if(k==4&&i4==a[4])continue;
tf=f[k][i0][i1][i2][i3][i4];
tp=p[k][i0][i1][i2][i3][i4];
tf/=tp;tp/=j;
if(tp==0)continue;
if(i0>0)
f[0][i0-1][ i1 ][ i2 ][ i3 ][ i4 ]+=(tf+(k!=0))*tp*i0,
p[0][i0-1][ i1 ][ i2 ][ i3 ][ i4 ]+=tp*i0;
if(i1>0)
f[1][ i0 ][i1-1][ i2 ][ i3 ][ i4 ]+=(tf+(k!=1))*tp*i1,
p[1][ i0 ][i1-1][ i2 ][ i3 ][ i4 ]+=tp*i1;
if(i2>0)
f[2][ i0 ][ i1 ][i2-1][ i3 ][ i4 ]+=(tf+(k!=2))*tp*i2,
p[2][ i0 ][ i1 ][i2-1][ i3 ][ i4 ]+=tp*i2;
if(i3>0)
f[3][ i0 ][ i1 ][ i2 ][i3-1][ i4 ]+=(tf+(k!=3))*tp*i3,
p[3][ i0 ][ i1 ][ i2 ][i3-1][ i4 ]+=tp*i3;
if(i4>0)
f[4][ i0 ][ i1 ][ i2 ][ i3 ][i4-1]+=(tf+(k!=4))*tp*i4,
p[4][ i0 ][ i1 ][ i2 ][ i3 ][i4-1]+=tp*i4;
}
tf=0;
for(i=0;i<N;i++)
tf+=f[i][0][0][0][0][0];
printf("%.10lf\n",tf);
return 0;
}
题解:
正反跑一遍最短路(第一次跑从起点带女朋友的,第二次跑从终点自己跑的)
在正向最短路生成的最短路树上跑\(bfs\),记录\(fa1[i][j]\),表示\(i\)的第\(1<<j\)个父亲,记录\(fa2[i][j]\),表示\(i\)点到\(1<<j\)个父亲中,单独回家的最小值,倍增处理一下.\(bfs\)时判断是否是最短路上的点,然后加入队列更新就行了
\(code:\)
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<ctype.h>
#define inf 1e9+9
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc()
{
// return getchar();
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
inline void read(T &x)
{
char tt;
while(!isdigit(tt=gc()));x=tt-'0';
while(isdigit(tt=gc())) x=x*10+tt-'0';
}
struct node{
int x,len;
inline node(int a,int b)
{
x=a;
len=b;
}
inline bool operator<(node a)const
{
return len>a.len;
}
};
int n,m,tot,t,maxn;
int dep[100005];
int dis1[100005],dis2[100005];
int fa1[100005][20],fa2[100005][20];
vector<node>G[100005][2];
priority_queue<node>q;
queue<int>qq;
void djs1()
{
for(int i=1;i<=n;i++) dis1[i]=inf;
dis1[1]=0;
q.push(node(1,0));
while(!q.empty())
{
int x=q.top().x;
int len=q.top().len;
q.pop();
if(len!=dis1[x]) continue;
for(int i=G[x][0].size()-1;i>=0;i--)
{
int p=G[x][0][i].x;
len=G[x][0][i].len;
if(dis1[p]<=dis1[x]+len) continue;
dis1[p]=dis1[x]+len;
q.push(node(p,dis1[p]));
}
}
}
void djs2()
{
for(int i=1;i<=n;i++) dis2[i]=inf;
dis2[n]=0;
q.push(node(n,0));
while(!q.empty())
{
int x=q.top().x;
int len=q.top().len;
q.pop();
if(len!=dis2[x]) continue;
for(int i=G[x][1].size()-1;i>=0;i--)
{
int p=G[x][1][i].x;
len=G[x][1][i].len;
if(dis2[p]<=dis2[x]+len) continue;
dis2[p]=dis2[x]+len;
q.push(node(p,dis2[p]));
}
}
}
void bfs()
{
qq.push(1);
while(!qq.empty())
{
int x=qq.front();
qq.pop();
for(int j=1;j<=maxn;j++)
{
fa1[x][j]=fa1[fa1[x][j-1]][j-1];
fa2[x][j]=min(fa2[fa1[x][j-1]][j-1],fa2[x][j-1]);
}
for(int i=G[x][0].size()-1;i>=0;i--)
{
int p=G[x][0][i].x;
int len=G[x][0][i].len;
if(dis1[p]==dis1[x]+len)
{
fa1[p][0]=x;
dep[p]=dep[x]+1;
qq.push(p);
}
}
}
}
void solve1(int x,int y)//街道
{
int ans=inf;
if(dep[x]<y)
{
puts("-1");
return;
}
for(int i=maxn;i>=0;i--)
if(dep[fa1[x][i]]>=y)
{
ans=min(ans,fa2[x][i]);
x=fa1[x][i];
}
ans=min(ans,fa2[x][0]);
printf("%d\n",ans);
}
void solve2(int x,int y)//时间
{
int ans=inf;
if(dis1[x]<y)
{
puts("-1");
return;
}
for(int i=maxn;i>=0;i--)
if(dis1[fa1[x][i]]>=y)
{
ans=min(ans,fa2[x][i]);
x=fa1[x][i];
}
ans=min(ans,fa2[x][0]);
printf("%d\n",ans);
}
int main()
{
read(n),read(m),read(t);
while(n>>maxn)maxn++;maxn--;
for(int i=1;i<=m;i++)
{
int x,y,len1,len2;
read(x),read(y),read(len1),read(len2);
G[x][0].push_back(node(y,len1));
G[y][0].push_back(node(x,len1));
G[x][1].push_back(node(y,len2));
G[y][1].push_back(node(x,len2));
}
djs1();// 1
djs2();// n
for(int i=1;i<=n;i++) fa2[i][0]=dis2[i];
bfs();
dis1[0]=-1;dep[0]=-1;
while(t--)
{
int k,x,y;
read(k),read(x),read(y);
if(k==1) solve1(x,y);
if(k==2) solve2(x,y);
}
}