T1:管理
对
998244
8
53
998244{\color{red}{8}}53
998244853取模
题解:
知道至少要多少还是不好做,能不能把它转化成暂时只计算这么多个呢?
是可以的,因为在一棵树的右链上接一棵子树不会改变原树的dfs序的,所以我们只需要记下右链的长度即可;因为限制关系形成了树形结构,记
s
i
z
[
i
]
siz[i]
siz[i]为
i
i
i至少需要管辖的人数,记
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
[
i
,
i
+
s
i
z
[
i
]
−
1
]
[i,i+siz[i]-1]
[i,i+siz[i]−1]形成的树的右链长度为
j
j
j的方案数,转移就按顺序将
i
i
i在限制树中的子树合并到
i
i
i上即可,由
f
[
i
]
[
x
∼
s
i
z
[
i
]
]
∗
f
[
j
]
[
y
]
→
f
[
i
]
[
x
+
y
]
f[i][x\sim siz[i]]*f[j][y]\to f[i][x+y]
f[i][x∼siz[i]]∗f[j][y]→f[i][x+y],记录一下后缀和,就是
O
(
n
2
)
O(n^2)
O(n2)的。
Code:
#include<bits/stdc++.h>
#define maxn 4505
using namespace std;
const int mod = 998244853;
int n,a[maxn],fa[maxn],siz[maxn],sum[maxn],f[maxn][maxn];
int main()
{
freopen("administration.in","r",stdin);
freopen("administration.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
a[1]=n;
for(int i=n;i>=1;i--){
if((a[i]+=i-1)>n) return puts("0"),0;
fa[i]=i,a[i]=a[fa[a[i]]];//fa[i]:i在哪个限制的范围中,a[i]:i的限制范围
f[i][siz[i]=1]=1;
for(int j=i+1;j<=a[i];j=a[j]+1){
sum[siz[i]+1]=0;
for(int k=siz[i];k>=1;k--) sum[k]=(sum[k+1]+f[i][k])%mod,f[i][k]=0;
for(int x=1;x<=siz[i];x++)
for(int y=1;y<=siz[j];y++)
f[i][x+y]=(f[i][x+y]+1ll*sum[x]*f[j][y])%mod;
siz[i]+=siz[j];
}
for(int j=i+1;j<=a[i];j++) fa[j]=i;
}
int ans=0;
for(int i=1;i<=n;i++) ans=(ans+f[1][i])%mod;
printf("%d\n",ans);
}
T2:
题解:
这种水流题要是陷入模拟的怪圈之中就会被各种特殊情况恶心致死。
正确的姿势应该是枚举最后水的范围
[
L
,
R
]
[L,R]
[L,R],然后硬把所有的水都聚集到这里来。值得注意的是如果考虑
L
−
1
L-1
L−1和
R
+
1
R+1
R+1,那么水的高度可能并不具有单调性,我们可先不考虑两边算出水的高度,然后再与两边比较,如果高了就将
[
L
,
R
]
[L,R]
[L,R]挖低。
记
f
(
l
,
r
)
f(l,r)
f(l,r)为水的区间为
[
l
,
r
]
[l,r]
[l,r]时水的高度,那么显然
f
(
l
,
r
)
≥
f
(
l
,
r
+
1
)
f(l,r)\ge f(l,r+1)
f(l,r)≥f(l,r+1),考虑固定
l
l
l,扫描
r
r
r,那么就可以令高度从1000在整数范围往下递减,算答案的时候根据在水下的块数补上小数部分的答案。复杂度
O
(
n
2
)
O(n^2)
O(n2)。
Code:
#include<bits/stdc++.h>
#define maxn 5005
using namespace std;
int T,n,lp,rp,ml[maxn],mr[maxn],sl[maxn],sr[maxn],S[maxn],a[maxn],w[maxn],cnt[1005];
int main()
{
freopen("exploration.in","r",stdin);
freopen("exploration.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d",&n),a[0]=a[n+1]=1e9,w[0]=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),S[i]=S[i-1]+a[i];
for(int i=1;i<=n;i++) scanf("%d",&w[i]),w[0]+=w[i];
for(int i=1;i<=n;i++)
if(!w[i]) ml[i]=sl[i]=0;
else{
lp=i,ml[i]=a[i],sl[i]=0;
for(int j=i+1;j<=n;j++) ml[j]=min(ml[j-1],a[j]);
for(int j=i+1;j<=n;j++) sl[j]=sl[j-1]+a[j]-ml[j];
break;
}
for(int i=n;i>=1;i--)
if(!w[i]) mr[i]=sr[i]=0;
else{
rp=i,mr[i]=a[i],sr[i]=0;
for(int j=i-1;j>=1;j--) mr[j]=min(mr[j+1],a[j]);
for(int j=i-1;j>=1;j--) sr[j]=sr[j+1]+a[j]-mr[j];
break;
}
double ans=1e9;
for(int l=1;l<=n;l++){
memset(cnt,0,sizeof cnt);
int h=1000,down=0,cd=0;
for(int r=l;r<=n;r++){
if(a[r]<=h){
down+=h-a[r],cnt[a[r]]++,cd++;
while(down>w[0]) down-=cd-=cnt[h--];
}
double x=h+1.0*(w[0]-down)/cd;
double res=S[r]-S[l-1]-x*(r-l+1)+w[0];
int lh=a[l-1],rh=a[r+1];
if(lp<l) lh=ml[l-1],res+=sl[l-1];
if(r<rp) rh=mr[r+1],res+=sr[r+1];
if(x>min(lh,rh)) res+=(x-min(lh,rh))*(r-l+1);
ans=min(ans,res);
}
}
printf("%.5f\n",ans);
}
}
T3:谈判
n
≤
500
,
w
i
≤
1
0
6
n≤500,w_i≤10^6
n≤500,wi≤106
题解:
(原题CF1061E)
两棵树,集合限制,带权
⇒
\Rarr
⇒ 费用流。
S
S
S向第一棵树的限制点连容量为
b
[
i
]
−
∑
j
是
j
到
i
路
径
上
的
第
一
个
限
制
点
b
[
j
]
b[i]-\sum_{j是j到i路径上的第一个限制点} b[j]
b[i]−∑j是j到i路径上的第一个限制点b[j],费用为0的边。
第二棵树的限制点向
T
T
T连容量为
b
[
i
]
−
∑
j
是
j
到
i
路
径
上
的
第
一
个
限
制
点
b
[
j
]
b[i]-\sum_{j是j到i路径上的第一个限制点} b[j]
b[i]−∑j是j到i路径上的第一个限制点b[j],费用为0的边。
设每个城市在第一棵树中被
x
x
x管辖,在第二棵树中被
y
y
y管辖,则
x
x
x向
y
y
y连容量为1,费用为
w
[
i
]
w[i]
w[i]的边。
跑最大费用最大流即可。
注意
b
[
i
]
b[i]
b[i]可以为0,所以初值设为-1.
Code:
#include<bits/stdc++.h>
#define maxn 1005
#define maxm 5005
using namespace std;
const int inf = 0x3f3f3f3f;
int n,S,T,X,Y,a[maxn];
int fir[maxn],nxt[maxm],to[maxm],c[maxm],w[maxm],tot=1;
inline void line(int x,int y,int z,int v){
nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=z,w[tot]=v;
nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0,w[tot]=-v;
}
void Fail(){puts("-1");exit(0);}
struct Tree{
vector<int>G[maxn];
int b[maxn],bel[maxn]; bool flg;
void link(int x,int y){G[x].push_back(y),G[y].push_back(x);}
int dfs(int u,int ff,int top){
int sum=0; bel[u]=top;
for(int i=0,lim=G[u].size(),v;i<lim;i++) if((v=G[u][i])!=ff)
sum+=dfs(v,u,b[v]?v:top);
if(b[u]){
if(b[u]<sum) Fail();
if(!flg) line(S,u,b[u]-sum,0);
else line(u+n,T,b[u]-sum,0);
return b[u];
}
return sum;
}
}T1,T2;
int pre[maxn],E[maxn],dis[maxn]; bool inq[maxn];
queue<int>q;
bool SPFA(){
memset(dis,-0x3f,(T+1)<<2);
dis[S]=0,q.push(S),pre[T]=-1;
while(!q.empty()){
int u=q.front(); q.pop(),inq[u]=0;
for(int i=fir[u],v;i;i=nxt[i]) if(c[i]&&dis[v=to[i]]<dis[u]+w[i]){
dis[v]=dis[u]+w[i],pre[v]=u,E[v]=i; if(!inq[v]) inq[v]=1,q.push(v);
}
}
return ~pre[T];
}
int Costflow(){
int ans=0,flow=0;
while(SPFA()){
int mn=inf;
for(int i=T;i!=S;i=pre[i]) mn=min(mn,c[E[i]]);
for(int i=T;i!=S;i=pre[i]) c[E[i]]-=mn,c[E[i]^1]+=mn;
ans+=dis[T]*mn,flow+=mn;
}
if(flow!=T1.b[X]) Fail();
return ans;
}
int main()
{
freopen("negotiation.in","r",stdin);
freopen("negotiation.out","w",stdout);
int x,y,m;
scanf("%d%d%d",&n,&X,&Y),S=0,T=2*n+1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++) scanf("%d%d",&x,&y),T1.link(x,y);
for(int i=1;i<n;i++) scanf("%d%d",&x,&y),T2.link(x,y);
scanf("%d",&m);
while(m--) scanf("%d%d",&x,&y),T1.b[x]=y;
scanf("%d",&m);
while(m--) scanf("%d%d",&x,&y),T2.b[x]=y;
if(T1.b[X]!=T2.b[Y]) Fail();
T1.dfs(X,0,X),T2.flg=1,T2.dfs(Y,0,Y);
for(int i=1;i<=n;i++) line(T1.bel[i],T2.bel[i]+n,1,a[i]);
printf("%d\n",Costflow());
}