比赛链接:https://codeforces.com/contest/908。
D. New Year and Arbitrary Arrangement
期望$dp$一般考虑从当前状态到终点状态的期望,不然有可能会出问题。
所以设$f_{i,j}$表示当前有$i$个$a$,$j$个$ab$,把剩下的东西补齐最后的$ab$个数的期望。
边界就是$f_{i,j}=j~(j\geqslant k)$。
还有就是当$i\geqslant k$的时候,用无穷级数算一下期望放多少个$a$之后会放一个$b$。
注意到一开始可以无限的放$b$,但是对答案不造成影响,所以就硬点第一个是$a$,答案就是$f_{1,0}$。
转移用记忆化搜索实现即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 1e6+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
int pa,pb,k,r[1002][1002];
int qpow(int a,int x) { int res=1; for(;x;x>>=1,a=1ll*a*a%mod) if(x&1) res=1ll*res*a%mod; return res; }
int f(int a,int b) { if(b>=k) return b; if(a>=k) return (a+b+1ll*pa*qpow(pb,mod-2)%mod)%mod; if(r[a][b]) return r[a][b]; return r[a][b]=(1ll*pa*f(a+1,b)+1ll*pb*f(a,b+a)%mod)%mod; }
int main() { read(k),read(pa),read(pb);int t=qpow(pa+pb,mod-2); pa=1ll*pa*t%mod,pb=1ll*pb*t%mod; write(f(1,0)); return 0; }
|
E. New Year and Entity Enumeration
好神仙啊。。
考虑对于一个集合$s$,如果他要变成好的,要生成一些什么样的数。
对于第$i$位,设$f_i$表示能被生成的数中包含第$i$位的数中包含$1$的个数最小的那个是什么。
注意到因为有取反操作,所以每一个$f_i$都是有值的。
现在有一个结论是说,对于$i,j$,如果$f_i\ne f_j$,那么$f_i\&f_j=0$.
考虑反证,假设不等于$0$,首先显然$f_j$第$i$位不可能是$1$,否则$f_i\&f_j$一定比$f_i$更优,这一来就会更新$f_i$。
如果$f_j$的第$i$位为$0$,$f_i\&(\text{~}f_j)$就会更新$f_i$。
那么可以发现这个集合里的所有数都只有可能是若干个$f_i$或起来的,就是说这些个$f_i$是最小的单元。
那么现在回到题目,首先把$T$的$f_i$求出来,考虑我们可以通过引入一些数来任意的分割任意一个$f_i$,使其变成若干个更小的单元。
那么答案就是一堆贝尔数相乘,也就是说把$n$个元素分成任意堆的方案数。
贝尔数有个递推公式,不过我懒的想那个东西了,也可以不用那个公式直接把一堆的第二类斯特林数加起来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 1e3+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
char ss[52][maxn]; int cnt[maxn],fa[maxn],n,m,s[maxn][maxn],b[maxn];
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
void link(int x,int y) { x=find(x),y=find(y); if(x!=y) fa[x]=y,cnt[y]+=cnt[x]; }
int main() { read(n),read(m); for(int i=1;i<=m;i++) scanf("%s",ss[i]+1); for(int i=1;i<=n;i++) fa[i]=i,cnt[i]=1; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { int bo=1; for(int k=1;k<=m;k++) if(ss[k][i]!=ss[k][j]) bo=0; if(bo) link(i,j); } s[0][0]=1; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) s[i][j]=(1ll*j*s[i-1][j]%mod+s[i-1][j-1])%mod; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) b[i]=(b[i]+s[i][j])%mod; int ans=1; for(int i=1;i<=n;i++) if(fa[i]==i) ans=1ll*ans*b[cnt[i]]%mod; write(ans); return 0; }
|
F. New Year and Rainbow Roads
普及组贪心题。
考虑每个绿色的作为关键点,两个绿色点之间有两种连法:
- 绿蓝绿连成一条链,绿红绿一条链。
- 绿色相连,那么上面的两条链就可以分别断掉一条边。
特判下两头的非绿色点就好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include<bits/stdc++.h> using namespace std;
#define int long long
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 1e6+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
int n,d[maxn],p[maxn],top; char c[maxn];
signed main() { read(n); for(int i=1;i<=n;i++) { read(d[i]),scanf("%c",&c[i]); if(c[i]=='G') p[++top]=i; } if(!top) { int a=0,b=0,cc=0,dd=0; for(int i=1;i<=n;i++) if(c[i]=='B') cc=d[i],a=a?a:d[i]; else dd=d[i],b=b?b:d[i]; write(dd-b+cc-a);return 0; } int ans=0; for(int i=2;i<=top;i++) { int l=p[i-1]+1,r=p[i]-1,pre=p[i-1],t=0,tt=0,pp=p[i-1]; for(int j=l;j<=r;j++) if(c[j]=='R') t=max(t,d[j]-d[pre]),pre=j; else tt=max(tt,d[j]-d[pp]),pp=j; t=max(t,d[p[i]]-d[pre]),tt=max(tt,d[p[i]]-d[pp]); ans+=min(2*(d[p[i]]-d[p[i-1]]),3*(d[p[i]]-d[p[i-1]])-t-tt); } int p1=p[1],p2=p[1]; for(int i=p[1]-1;i;i--) if(c[i]=='R') ans+=d[p1]-d[i],p1=i; else ans+=d[p2]-d[i],p2=i; p1=p[top],p2=p[top]; for(int i=p[top];i<=n;i++) if(c[i]=='R') ans+=d[i]-d[p1],p1=i; else ans+=d[i]-d[p2],p2=i; write(ans); return 0; }
|
G. New Year and Original Order
观察下可以发现,对于一个排好序的数,我们对数位差分一下可以发现这个数是由九个形如$1111\cdots111$的数加起来得到的。
更具体的说,对于$x~(1\leqslant x\leqslant 9)$,如果有$k$个大于等于它的数位,那么他就贡献了一个$k$个$1$的数字。
那么直接数位$dp$,$f_{i,j,k,0/1}$表示当前$dp$了前$i$位,有$j$个大于等于$k$的数位,有没有贴紧上界的方案数。
转移很简单,暴力就好了。
感觉难度好不均匀啊,为啥第五题那么难,后面的简单些啊
果然是我太菜了吗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 700+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
int f[maxn][maxn][10][2],n; char s[maxn];
void add(int &x,int y) {x+=y;if(x>=mod) x-=mod;}
int main() { scanf("%s",s+1),n=strlen(s+1); for(int i=1;i<=n;i++) s[i]-='0'; for(int i=1;i<=9;i++) f[0][0][i][1]=1; for(int k=1;k<=9;k++) for(int i=1;i<=n;i++) for(int j=0;j<=i;j++) { if(j||s[i]<k) add(f[i][j][k][1],f[i-1][j-(s[i]>=k)][k][1]); add(f[i][j][k][0],1ll*f[i-1][j][k][0]*k%mod); if(j) add(f[i][j][k][0],1ll*f[i-1][j-1][k][0]*(10-k)%mod); add(f[i][j][k][0],1ll*f[i-1][j][k][1]*min(k,(int)s[i])%mod); if(j) add(f[i][j][k][0],1ll*f[i-1][j-1][k][1]*max(s[i]-k,0)%mod); } int ans=0; for(int k=1;k<=9;k++) { int r=0; for(int i=1;i<=n;i++) { r=(1ll*r*10+1)%mod; ans=(ans+1ll*r*(f[n][i][k][0]+f[n][i][k][1])%mod)%mod; } }write(ans); return 0; }
|
H. New Year and Boolean Bridges
这个图显然是弱联通的。
容易发现$\rm AND$表示这两个点强连通,那么可以先缩一波点,对于每个$size\geqslant 2$的强连通块,最少需要$size$条边把他们连成一个环。
剩下的连成一条链就可以满足条件了。
考虑怎么减少边数,显然如果我们把两个强连通块合并,就会少一条链的边,因为强连通块要求$size\geqslant 2$,所以一共只有$n/2$个,可以状压。
设$f_s$表示$s$这些点可不可以弄成一个强连通块,这是因为$\rm XOR$关系不能在一个强连通块中。
设$g_{k,s}$表示用$k$条链边能不能把$s$这些点连起来。
那么$g_{k,s}=\sum_{t\subset s} g_{k-1,t}f_{s-t}$。
显然这可以看做是一个或卷积,因为$s-t$和$t$实际上如果重合了答案也不会错。
所以每次拿$g$卷上$f$,直到$g_{all-1}$不为$0$就得到了最优解。
那么使用$\rm fwt$优化即可,复杂度$O(n^22^{n/2})$。
复杂度还是有点高,考虑如何优化,注意到不需要每次都$\rm fwt$,因为$\rm fwt$操作其实是子集和,所以我们可以每次利用子集容斥把$g_{all-1}$一项搞出来,那么只需要$\rm fwt$一次即可。
复杂度$O(n\cdot 2^{n/2})$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 50+10; const int maxm = (1<<23)+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
char s[maxn][maxn]; int n,fa[maxn],sz[maxn],id[maxn],m; int f[maxm],g[maxm],a[maxm];
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
void get(int a,int b) { a=find(a),b=find(b); if(a!=b) fa[a]=b,sz[b]+=sz[a]; }
void fwt(int *r) { for(int i=1;i<1<<m;i<<=1) for(int j=0;j<1<<m;j+=i<<1) for(int k=0;k<i;k++) r[i+j+k]+=r[j+k]; }
int main() { read(n);for(int i=1;i<=n;i++) scanf("%s",s[i]+1),fa[i]=i,sz[i]=1; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(s[i][j]=='A') get(i,j); for(int i=1,v;i<=n;i++) if((v=find(i))==i) if(sz[v]>=2) id[v]=++m; if(!m) return write(n-1),0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(s[i][j]=='X') { int u=find(i),v=find(j); if(u==v) return puts("-1"),0; if(!id[u]||!id[v]) continue; f[(1<<(id[u]-1))|(1<<(id[v]-1))]=1; } fwt(f); for(int i=0;i<1<<m;i++) f[i]=!f[i]; fwt(f); for(int i=0;i<1<<m;i++) g[i]=f[i],a[i]=1; for(int x=0;;x++) { int t=0; for(int i=0;i<1<<m;i++) t+=(__builtin_popcount((1<<m)-1-i)&1?1:-1)*g[i]; if(t) return write(n+x),0; for(int i=0;i<1<<m;i++) g[i]=g[i]*f[i]; } return 0; }
|