A. coin
分析
设带某种礼物 (x) 件的贡献是 (f(x)) 那么有 (f(x+2)−f(x+1) leq f(x+1)−f(x))
那么就是说每多一件礼物,收益是递减的
所以开一个堆,维护 ((i,j,w)) 表示第 (i) 种礼物选第 (j) 份的收益是 (w)
贪心的选最大的就好了
问题在于求出 (w)
设 (f(k,c,n)) 表示对于前 (n) 个人第 (k) 种礼物一共有 (c) 份的贡献
(w=f(i,j,n)−f(i,j−1,n))
直接 (dp) 是 (n^2m) 的
但是可以发现有用的不是很多,所以把 (dp) 的形式改成记忆化搜索,需要的时候再搜
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#include<vector>
#include<queue>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=3e3+5,maxm=3e2+5;
int n,m;
double p[maxn][maxm],ans;
std::vector<double> g[maxm][maxn];
double getf(rg int k,rg int c,rg int nn){
while(nn && !p[nn][k]) nn--;
if(c==0 || !nn) return 0;
if(g[k][c][nn]) return g[k][c][nn];
return g[k][c][nn]=(getf(k,c-1,nn-1)+1)*p[nn][k]+getf(k,c,nn-1)*(1.0-p[nn][k]);
}
struct jie{
int num,tim;
double val;
jie(){}
jie(rg int aa,rg int bb,rg double cc){
num=aa,tim=bb,val=cc;
}
bool operator <(const jie& A)const{
return val<A.val;
}
};
std::priority_queue<jie> q;
int main(){
n=read(),m=read();
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=m;j++){
p[i][j]=read();
p[i][j]/=1000.0;
}
}
for(rg int i=1;i<=m;i++){
g[i][0].resize(n+1),g[i][1].resize(n+1);
q.push(jie(i,1,getf(i,1,n)));
}
rg int aa,bb;
rg double cc;
for(rg int i=1;i<=n;i++){
aa=q.top().num,bb=q.top().tim,cc=q.top().val;
q.pop();
ans+=cc;
g[aa][bb+1].resize(n+1);
q.push(jie(aa,bb+1,getf(aa,bb+1,n)-getf(aa,bb,n)));
}
printf("%.10f
",ans);
return 0;
}
B. B
分析
把问题转化一下,要求的就是与给定树重合边数 (geq n−1−k) 的树的个数
众所周知矩阵树求的是这个
(sum_{T}prod_{ein T}w_e)
把原树中的边权设为 (x),其它的边权设为 (1)
代入矩阵树定理得到的是一个多项式
多项式的 (k) 次方项的系数就是重合边为 (k) 条时的答案
多项式显然是一个 (n-1) 次多项式
可以代入 (n) 项求出值,然后用高斯消元算出系数
这样做复杂度是 (n^4) 的
口胡一下 (n^2) 的做法
首先钦定 (k) 条边重合,那么可以用二项式反演得到恰好有 (k) 条边重合的方案
有一个结论 :设有 (m) 个连通块,每个连通块大小分别为 (size_i),生成树个数就是 (n^{m-2} prod limits_{i=1}^m size_i)
发现后面 (size) 的乘积不好处理
考虑 (prodlimits_{i=1}^msize_i) 的组合意义,我们可以视为将树划分为若干个联通块,每个联通块内放入一个特殊点的方案数
改变一下 (dp) 定义,(dp_{i,j,0/1}) 表示 (i) 子树中,划分了 (j) 个连通块,最后一个连通块是否选择了特殊点
这样复杂度就是 (n^2) 的
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<vector>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=55,mod=998244353;
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
inline int ksm(rg int ds,rg int zs){
rg int nans=1;
while(zs){
if(zs&1) nans=mulmod(nans,ds);
ds=mulmod(ds,ds);
zs>>=1;
}
return nans;
}
int mp[maxn][maxn],e[maxn][maxn],ans[maxn],tmp[maxn][maxn],n,m;
int solve(rg int val){
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=n+1;j++){
mp[i][j]=0;
}
}
for(rg int i=1;i<=n;i++){
for(rg int j=i+1;j<=n;j++){
if(e[i][j]){
mp[i][i]=addmod(mp[i][i],val);
mp[j][j]=addmod(mp[j][j],val);
mp[i][j]=delmod(mp[i][j],val);
mp[j][i]=delmod(mp[j][i],val);
} else {
mp[i][i]=addmod(mp[i][i],1);
mp[j][j]=addmod(mp[j][j],1);
mp[i][j]=delmod(mp[i][j],1);
mp[j][i]=delmod(mp[j][i],1);
}
}
}
rg int nans=1;
for(rg int i=2;i<=n;i++){
for(rg int j=i+1;j<=n;j++){
if(!mp[i][i] && mp[j][i]){
std::swap(mp[i],mp[j]);
nans=delmod(0,nans);
break;
}
}
rg int ny=ksm(mp[i][i],mod-2);
for(rg int j=i+1;j<=n;j++){
rg int tmp=mulmod(ny,mp[j][i]);
for(rg int k=i;k<=n+1;k++) mp[j][k]=delmod(mp[j][k],mulmod(mp[i][k],tmp));
}
}
for(rg int i=2;i<=n;i++) nans=mulmod(nans,mp[i][i]);
return nans;
}
int main(){
n=read(),m=read();
rg int aa;
for(rg int i=2;i<=n;i++){
aa=read()+1;
e[i][aa]=e[aa][i]=1;
}
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=n;j++){
tmp[i][j]=ksm(i,j-1);
}
tmp[i][n+1]=solve(i);
}
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=n+1;j++){
mp[i][j]=tmp[i][j];
}
}
for(rg int i=1;i<=n;i++){
rg int ny=ksm(mp[i][i],mod-2);
for(rg int j=i;j<=n+1;j++) mp[i][j]=mulmod(mp[i][j],ny);
for(rg int j=i+1;j<=n;j++){
rg int tmp=mp[j][i];
for(rg int k=i;k<=n+1;k++) mp[j][k]=delmod(mp[j][k],mulmod(mp[i][k],tmp));
}
}
ans[n]=mp[n][n+1];
for(rg int i=n-1;i>=1;i--){
ans[i]=mp[i][n+1];
for(rg int j=i+1;j<=n;j++){
ans[i]=delmod(ans[i],mulmod(ans[j],mp[i][j]));
}
}
rg int sum=0;
for(rg int i=n-m;i<=n;i++) sum=addmod(sum,ans[i]);
printf("%d
",sum);
return 0;
}
C. battery
分析
发现不确定的只有炮台的方向
而炮台的方向只有两种,所以可以看成一个 (2-SAT) 问题
因为炮台之间不能相互攻击,所以合法的情况下一个空地最多只能由两个炮台经过
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<vector>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=105,maxm=1e4+5,dx[6]={1,-1,0,0},dy[6]={0,0,1,-1};
int h[maxm],tot=1;
struct asd{
int to,nxt;
}b[maxm];
void ad(rg int aa,rg int bb){
b[tot].to=bb;
b[tot].nxt=h[aa];
h[aa]=tot++;
}
int dfn[maxm],low[maxm],dfnc,sta[maxm],tp,shuyu[maxm],js;
void tar(rg int xx){
dfn[xx]=low[xx]=++dfnc;
sta[++tp]=xx;
for(rg int i=h[xx];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(!dfn[u]){
tar(u);
low[xx]=std::min(low[xx],low[u]);
} else if(!shuyu[u]){
low[xx]=std::min(low[xx],dfn[u]);
}
}
if(low[xx]==dfn[xx]){
js++;
while(1){
rg int y=sta[tp--];
shuyu[y]=js;
if(y==xx) break;
}
}
}
int t,n,m,rk1[maxn][maxn],rk2[maxn][maxn],vx1[maxm],vx2[maxm],vy1[maxm],vy2[maxm],cnt1,cnt2,jud,haha;
std::vector<int> g[maxm],tmp;
char s[maxn][maxn];
void dfs(rg int nx,rg int ny,rg int d){
if(s[nx][ny]=='#' || nx>n || nx<1 || ny>m || ny<1 || !jud) return;
else if(s[nx][ny]=='.'){
tmp.push_back(rk2[nx][ny]);
dfs(nx+dx[d],ny+dy[d],d);
} else if(s[nx][ny]=='\'){
dfs(nx+dx[d^2],ny+dy[d^2],d^2);
} else if(s[nx][ny]=='/'){
dfs(nx+dx[3-d],ny+dy[3-d],3-d);
} else {
jud=0;
}
}
int main(){
t=read();
while(t--){
memset(h,-1,sizeof(h));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(shuyu,0,sizeof(shuyu));
for(rg int i=1;i<=cnt2;i++) g[i].clear();
tot=haha=1,dfnc=tp=js=cnt1=cnt2=0;
n=read(),m=read();
for(rg int i=1;i<=n;i++) scanf("%s",s[i]+1);
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=m;j++){
if(s[i][j]=='.'){
cnt2++;
vx2[cnt2]=i,vy2[cnt2]=j,rk2[i][j]=cnt2;
} else if(s[i][j]=='-' || s[i][j]=='|'){
cnt1++;
vx1[cnt1]=i,vy1[cnt1]=j,rk1[i][j]=cnt1;
}
}
}
rg int tmp1=0,tmp2=0;
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=m;j++){
if(s[i][j]=='|' || s[i][j]=='-'){
tmp1=tmp2=0,jud=1;
tmp.clear(),dfs(i+1,j,0);
dfs(i-1,j,1);
if(jud) for(rg int o=0;o<tmp.size();o++) g[tmp[o]].push_back(rk1[i][j]);
tmp1=jud,jud=1;
tmp.clear(),dfs(i,j+1,2);
dfs(i,j-1,3);
if(jud) for(rg int o=0;o<tmp.size();o++) g[tmp[o]].push_back(rk1[i][j]+cnt1);
tmp2=jud;
if(!tmp1 && !tmp2) haha=0;
if(!tmp1){
ad(rk1[i][j],rk1[i][j]+cnt1);
}
if(!tmp2){
ad(rk1[i][j]+cnt1,rk1[i][j]);
}
}
}
}
for(rg int i=1;i<=cnt2;i++){
if(g[i].size()==0) haha=0;
else if(g[i].size()==1){
if(g[i][0]>cnt1) ad(g[i][0]-cnt1,g[i][0]);
else ad(g[i][0]+cnt1,g[i][0]);
} else {
if(g[i][0]>cnt1) ad(g[i][0]-cnt1,g[i][1]);
else ad(g[i][0]+cnt1,g[i][1]);
if(g[i][1]>cnt1) ad(g[i][1]-cnt1,g[i][0]);
else ad(g[i][1]+cnt1,g[i][0]);
}
}
for(rg int i=1;i<=cnt1+cnt1;i++){
if(!dfn[i]) tar(i);
}
for(rg int i=1;i<=cnt1;i++) if(shuyu[i]==shuyu[i+cnt1]) haha=0;
if(!haha) printf("IMPOSSIBLE
");
else {
printf("POSSIBLE
");
for(rg int i=1;i<=cnt1;i++){
if(shuyu[i]>shuyu[i+cnt1]) s[vx1[i]][vy1[i]]='-';
else s[vx1[i]][vy1[i]]='|';
}
for(rg int i=1;i<=n;i++) s[i][m+1]=' ';
for(rg int i=1;i<=n;i++) printf("%s
",s[i]+1);
}
}
return 0;
}