A - Alice the Fan
思路: 记忆化搜索
根据规则记忆化搜索即可…
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
//(25):(0~23),(0~23):25,(x+2):(x),(x):(x+2)
//25:23 69,(21)
bool can[12][205][205]={0},fid[7][12][205][205]={0};
int mem[6][2];
int fan[12][205][205][6][2],shengfu[12][205][205][2];
void dfs(int t,int d,int a,int b,int x,int y){
if(t>6||a>200||b>200||can[d][a][b])return ;
if(x==3||y==3){
can[d][a][b]=1;
for(int i=1;i<=5;i++){
fan[d][a][b][i][0]=mem[i][0];
fan[d][a][b][i][1]=mem[i][1];
}
shengfu[d][a][b][0]=x;
shengfu[d][a][b][1]=y;
return ;
}
if(t>5||fid[t][d][a][b])return ;
if(t<=4){
for(int i=0;i<=23;i++){
mem[t][0]=i;mem[t][1]=25;
dfs(t+1,d-1,a+i,b+25,x,y+1);
mem[t][0]=25;mem[t][1]=i;
dfs(t+1,d+1,a+25,b+i,x+1,y);
}
for(int i=24;i<=198;i++){
mem[t][0]=i;mem[t][1]=i+2;
dfs(t+1,d-1,a+i,b+i+2,x,y+1);
mem[t][0]=i+2;mem[t][1]=i;
dfs(t+1,d+1,a+i+2,b+i,x+1,y);
}
}
if(t==5){
for(int i=0;i<=13;i++){
mem[t][0]=i;mem[t][1]=15;
dfs(t+1,d-1,a+i,b+15,x,y+1);
mem[t][0]=15;mem[t][1]=i;
dfs(t+1,d+1,a+15,b+i,x+1,y);
}
for(int i=14;i<=198;i++){
mem[t][0]=i;mem[t][1]=i+2;
dfs(t+1,d-1,a+i,b+i+2,x,y+1);
mem[t][0]=i+2;mem[t][1]=i;
dfs(t+1,d+1,a+i+2,b+i,x+1,y);
}
}
fid[t][d][a][b]=1;
return ;
}
int main(){
int n;
dfs(1,5,0,0,0,0);
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
bool pos=0;
//for(int i=3;i>=0;i--){
for(int i=3;i>=-3;i--){
if(can[i+5][a][b]){
pos=1;
printf("%d:%d\n",shengfu[i+5][a][b][0],shengfu[i+5][a][b][1]);
int q=shengfu[i+5][a][b][0]+shengfu[i+5][a][b][1];
for(int j=1;j<=q;j++){
printf("%d:%d%s",fan[i+5][a][b][j][0],fan[i+5][a][b][j][1],(j==q)?"\n":" ");
}
break;
}
}
if(!pos){
puts("Impossible");
}
}
}
B - Barber Shop
思路: 公式+暴力
设一共有
k
k
k个分量
P
1
,
P
2
,
…
,
P
k
P_1,P_2,\dots,P_k
P1,P2,…,Pk,那么:
r
e
s
=
(
n
∣
P
1
∣
∣
P
2
∣
…
∣
P
k
∣
)
∏
i
=
1
k
g
(
∣
P
k
∣
+
1
)
.
res=\binom{n}{|P_1|\ |P_2|\ \dots\ |P_k|}\prod_{i=1}^k g(|P_k|+1).
res=(∣P1∣ ∣P2∣ … ∣Pk∣n)i=1∏kg(∣Pk∣+1).
其中
g
(
n
)
=
n
n
−
2
g(n)=n^{n-2}
g(n)=nn−2.
前面一个系数很好求出,是
n
n
n个不同的位置分配给
k
k
k个分量产生的.
后面一个如何理解?
想象一个序列
a
[
n
]
a[n]
a[n],值域是
[
1
,
n
]
[1,n]
[1,n],元素
i
i
i的个数不超过
i
i
i个.因此
1
∼
n
1\sim n
1∼n的个数被看作是
y
⩽
x
y\leqslant x
y⩽x的格子不同的走法的个数染色,我们从
(
0
,
0
)
(0,0)
(0,0)走到
(
n
,
n
)
(n,n)
(n,n),因此一共有
(
n
+
1
)
n
−
1
(n+1)^{n-1}
(n+1)n−1种不同的走法.
#include <cstdio>
const int N=200005;
const long long mod=1e9+7;
long long qp(long long a,long long b){
long long res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod; b>>=1;
}
return res;
}
int ans[N],a[N];
long long res,fac[N+5],inv[N+5];
long long CN(int *ans,int n){
long long res=1; for(int i=1;i<=n;i++)res=res*inv[ans[i]]%mod;
return res;
}
long long G(int *ans,int n){
long long res=1; for(int i=1;i<=n;i++)res=res*qp(ans[i]+1,ans[i]-1)%mod;
return res;
}
int main(){
int T;
fac[0]=1;
for(int i=1;i<=N;i++)fac[i]=fac[i-1]*i%mod;
inv[N]=qp(fac[N],mod-2);
for(int i=N;i>=2;i--)inv[i-1]=inv[i]*i%mod;
inv[0]=1;
scanf("%d",&T);
while(T--){
res=1;
int n,c=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
a[n+1]=0;
for(int i=1;i<=n;i++){
if(a[i]){
ans[++c]=0;
for(int suc=i,j;a[suc];j=a[suc],a[suc]=0,suc=j)ans[c]++;
}
}
printf("%I64d%s",(fac[n]*CN(ans,c)%mod)*G(ans,c)%mod,T?"\n":"");
}
}
C - Chef Counts Semi-BSTs
思路: 还没想出来
递推式:
f
[
i
]
=
{
∑
i
=
1
n
−
1
i
i
−
1
⋅
f
[
n
−
1
−
i
]
,
i
⩾
2
;
1
,
i
=
1.
f[i]=\left\{\begin{array}{ll} \displaystyle \sum_{i=1}^{n-1} i^{i-1}\cdot f[n-1-i]&,i\geqslant 2;\\ 1&,i=1. \end{array}\right.
f[i]=⎩⎪⎨⎪⎧i=1∑n−1ii−1⋅f[n−1−i]1,i⩾2;,i=1.
D - Mst 最小生成树
思路: 网络流
我们不妨加一条不在树上的边
x
x
x,并设形成了环
{
e
,
e
x
1
,
e
x
2
,
…
,
e
x
k
(
x
)
}
\{e,e_{x_1},e_{x_2},\dots,e_{x_{k(x)}}\}
{e,ex1,ex2,…,exk(x)}.
显然必须满足下列不等式
w
(
e
x
i
)
⩽
w
(
x
)
,
i
=
1
,
2
,
…
,
k
(
x
)
w(e_{x_i})\leqslant w(x),i=1,2,\dots,k(x)
w(exi)⩽w(x),i=1,2,…,k(x)
故,设正的变化函数
Δ
w
=
∣
w
′
−
w
∣
\Delta w=|w'-w|
Δw=∣w′−w∣:
∀
e
̸
∈
G
−
T
,
Δ
w
(
e
e
i
)
+
Δ
w
(
e
)
⩾
w
(
e
x
i
)
−
w
(
e
)
⩾
0
\forall e\not \in G-T,\Delta w(e_{e_i})+\Delta w(e)\geqslant w(e_{x_i})-w(e)\geqslant 0
∀e̸∈G−T,Δw(eei)+Δw(e)⩾w(exi)−w(e)⩾0
用最大费用最大流跑一遍即可…注意选择正确的模板…我zkw模板是错的,mcmf模板是对的…
#include <cstdio>
#include <queue>
#include <map>
#include <algorithm>
#include <cstring>
using namespace std;
//typedef long long ll;
typedef int flowType;
//const ll INF=0x3f3f3f3f3f3f3f3fLL;
const int INF=0x3f3f3f3f;
const int N=850,M=1000000;
const int Headsize=N,Edgesize=M;
int head[Headsize+5],mal;
struct edge{
int nx,to;flowType w,c;
}e[Edgesize+5];
inline void init(){
mal=0;
memset(head,-1,sizeof(head));
}
inline void _addedge(int u,int v,flowType val,flowType cost){
e[mal].to=v;e[mal].c=cost;e[mal].w=val;e[mal].nx=head[u];head[u]=mal++;
}
inline void addedge(int u,int v,flowType val,flowType cost){
_addedge(u,v,val,cost);_addedge(v,u,0,-cost);
}
struct MCMF{
bool vis[N];flowType dis[N],lnk[N];
int n,s,t;
inline void set(int _n,int _s,int _t){n=_n;s=_s;t=_t;}
bool spfa(){
memset(lnk, -1,sizeof(lnk));
memset(dis,192,sizeof(dis));
queue<int> Q;
Q.push(s);
dis[s]=0;vis[s]=1;
while(Q.size()){
int now=Q.front();Q.pop();
vis[now]=0;
for(int i=head[now];i+1;i=e[i].nx){
if(e[i].w&&dis[e[i].to]<dis[now]+e[i].c){
dis[e[i].to]=dis[now]+e[i].c;
lnk[e[i].to]=i;
if(!vis[e[i].to]){
vis[e[i].to]=1;
Q.push(e[i].to);
}
}
}
}
return dis[t]^dis[0];
}
int mcmf(int n,int s,int t){
set(n,s,t);
int mincost=0;
while(spfa()&&dis[t]>0){
int mw=INF;
for(int i=lnk[t];i+1;i=lnk[e[i^1].to])mw=min(mw,e[i].w);
for(int i=lnk[t];i+1;i=lnk[e[i^1].to])e[i].w-=mw,e[i^1].w+=mw;
mincost+=dis[t]*mw;
}
return mincost;
}
}netFlow;
int G[55][55],faz[55],dep[55];
int sig[55][55];
int n,m;
void dfs(int u,int f){
for(int v=1;v<=n;v++){
if(v==f)continue;
if(sig[u][v]){
dep[v]=dep[u]+1;
faz[v]=u;
dfs(v,u);
}
}
}
void cret(int x,int y,int w,int id){
if(x==y)return ;
if(dep[x]<dep[y])swap(x,y);
if(G[x][faz[x]]>w){
addedge(sig[x][faz[x]],id,1,G[x][faz[x]]-w);
}
cret(faz[x],y,w,id);
}
int main(){
dep[1]=0;
init();
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
G[u][v]=G[v][u]=w;
}
int qwq=n,s=1,t=m+2;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
sig[u][v]=sig[v][u]=i+1;
addedge(s,i+1,1,0);
}
dfs(1,0);
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(!sig[i][j]&&G[i][j]){
cret(i,j,G[i][j],++qwq);
addedge(qwq,t,1,0);
}
}
}
printf("%d\n",netFlow.mcmf(m+2,s,t));
}
E - King Kog’s Reception
思路: 线段树
考虑到若一个节点被影响当且仅当左侧链的最右侧覆盖了这个节点.
最右侧的计算有两个因素,一个是当前节点被影响的最右侧的值,一个是当前节点往后一长串覆盖的节点的工作时间之和.
这样就可以做了.
再考虑一个规约简化代码量:
第
i
i
i个骑士如果不在,那么我们设立空骑士这个骑士被影响的最右侧的值为
m
x
=
i
mx=i
mx=i,这个骑士需要面见国王的时间为
s
u
m
=
0
sum=0
sum=0.
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=1e6+5;
long long mx[N<<2],sum[N<<2];//,left[N<<2];
void pushup(int p){
sum[p]=sum[p<<1]+sum[p<<1|1];
mx[p]=max(mx[p<<1]+sum[p<<1|1],mx[p<<1|1]);
// if(left[p<<1]!=-1)left[p]=left[p<<1];
// else left[p]=left[p<<1|1];
// if(mx[p<<1]!=-1&&mx[p<<1]>=left[p<<1|1]){
// mx[p]=left[p]+sum[p];
// }else{
// mx[p]=mx[p<<1|1];
// }
}
long long b[N];
void build(int p,int l,int r){
if(l==r){
//left[p]=a[l];
sum[p]=b[l];
mx[p]=l+b[l];
//mx[p]=(a[l]==-1)?-1:(a[l]+b[l]);
return ;
}
int m=(l+r)>>1;
build(p<<1,l,m);
build(p<<1|1,m+1,r);
pushup(p);
}
void update(int p,int l,int r,int i){
if(l==r){
//left[p]=a[l];
sum[p]=b[l];
mx[p]=l+b[l];
//mx[p]=-1;
return ;
}
int m=(l+r)>>1;
if(i<=m)update(p<<1,l,m,i);
else update(p<<1|1,m+1,r,i);
pushup(p);
}
// void join(int p,int l,int r,int i){
// if(l==r){
// sum[p]=b[l];
// mx[p]=a[l]+b[l];
// // left[p]=a[l];
// // sum[p]=b[l];
// // mx[p]=a[l]+b[l];
// return ;
// }
// int m=(l+r)>>1;
// if(i<=m)join(p<<1,l,m,i);
// else join(p<<1|1,m+1,r,i);
// pushup(p);
// }
// int query(int p,int l,int r,int c,int offset){
// if(l==r){
// return (a[l]==-1)?0:b[l];
// }
// int m=(l+r)>>1;
// if(c<=m)return query(p<<1,l,m,c,offset);
// if(mx[p<<1]==-1)return query(p<<1,m+1,r,c,offset);
// if(left[p<<1|1]==-1)return (mx[p<<1]<=c)?0:(mx[p<<1]-c);
// if(mx[p<<1]>=left[p<<1|1])return
// }
long long res;
void query(int p,int l,int r,int c){
if(r<=c){
res=max(mx[p],res+sum[p]);
return ;
}
int m=(l+r)>>1;
query(p<<1,l,m,c);
if(m<c)query(p<<1|1,m+1,r,c);
return ;
}
char o[10];
int Time[N+5];
int main(){
int Q;
scanf("%d",&Q);
for(int i=1;i<=1000000;i++){
b[i]=0;
}
build(1,1,1000000);
for(int i=1;i<=Q;i++){
scanf("%s",o);
switch(o[0]){
case '+':{
int d;
scanf("%d%d",&Time[i],&d);
b[Time[i]]=d;
update(1,1,1000000,Time[i]);
break;
}
case '-':{
scanf("%d",&Time[i]);
b[Time[Time[i]]]=0;
update(1,1,1000000,Time[Time[i]]);
break;
}
case '?':{
scanf("%d",&Time[i]);
res=-1;
query(1,1,1000000,Time[i]);
printf("%I64d\n",res-Time[i]);
break;
}
}
}
}
F - Bi-shoe and Phi-shoe
思路: 任意积性筛法+递推
今天最简单的没想到是个数论题…
显然用埃氏筛就可以
O
(
n
l
g
l
g
n
)
O(nlglgn)
O(nlglgn)筛出
φ
\varphi
φ,每个人各自的答案就是
r
e
s
[
i
]
=
m
i
n
φ
−
1
(
i
)
res[i]=min \varphi^{-1}(i)
res[i]=minφ−1(i).
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1000050;
int res[1000050+5];
int phi[N+5];
void sieve(){
memset(res,0x3f,sizeof(res));
for(int i=1;i<=N;i++)phi[i]=i;
phi[1]=0;
for(int i=2;i<=N;i++){
if(phi[i]==i){
for(int j=i+i;j<=N;j+=i){
phi[j]=phi[j]/i*(i-1);
}
phi[i]=i-1;
}
}
for(int i=1;i<=N;i++){
res[phi[i]]=min(res[phi[i]],i);
}
for(int i=N-1;i>=1;i--){
res[i]=min(res[i],res[i+1]);
}
}
int main(){
sieve();
//printf("%d",(int)(2e10));
//printf("%d",phi[6]);
int T;
scanf("%d",&T);
int n;
for(int ttt=1;ttt<=T;ttt++){
scanf("%d",&n);
int a;
long long ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a);
ans+=res[a];
//printf("%d ",res[a]);
}
printf("Case %d: %lld Xukha\n",ttt,ans);
}
}
G - Archi and Tree
H - A Race Against Time
思路: 动态规划
设
g
(
i
,
j
)
=
∣
H
i
−
H
j
∣
−
i
+
j
+
d
p
[
j
]
+
P
j
,
g
(
i
,
M
a
d
i
s
o
n
)
=
n
+
1
−
i
g(i,j)=|H_i-H_j|-i+j+dp[j]+P_j,g(i,Madison)=n+1-i
g(i,j)=∣Hi−Hj∣−i+j+dp[j]+Pj,g(i,Madison)=n+1−i.
d
p
[
i
]
=
min
t
i
l
l
h
[
j
]
>
h
[
i
]
{
g
(
i
,
j
)
}
=
H
i
−
i
+
min
b
e
f
o
r
e
h
[
j
]
>
h
[
i
]
a
n
d
h
[
R
]
>
h
[
i
]
{
d
p
[
j
]
+
C
j
,
g
(
i
,
R
)
}
dp[i]=\min_{till\ h[j]>h[i]}\{g(i,j)\}=H_i-i+\min_{before\ h[j]>h[i]\ and\ h[R]>h[i]}\{dp[j]+C_j,g(i,R)\}
dp[i]=till h[j]>h[i]min{g(i,j)}=Hi−i+before h[j]>h[i] and h[R]>h[i]min{dp[j]+Cj,g(i,R)}
这里操作是倒着来的.
#include <cstdio>
#include <set>
#include <utility>
#include <algorithm>
using namespace std;
const int N=1e5+50;
long long h[N],sgt[N<<2],c[N],dp[N];
struct flow{
pair<int,int> S[N];
int top;
flow(){top=0;}
flow(pair<int,int> s){top=1;S[top]=s;}
int pushtill(pair<int,int> s){
while(S[top].first<=s.first)top--;
int t=S[top].second;
S[++top]=s;
return t;
}
};
void build(int p,int l,int r){
if(l==r){
sgt[p]=dp[l]+c[l];
return ;
}
int m=(l+r)>>1;
build(p<<1,l,m);
build(p<<1|1,m+1,r);
sgt[p]=min(sgt[p<<1],sgt[p<<1|1]);
}
void update(int p,int l,int r,int a){
if(l==r){
sgt[p]=dp[l]+c[l];
return ;
}
int m=(l+r)>>1;
if(a<=m)update(p<<1,l,m,a);
else update(p<<1|1,m+1,r,a);
sgt[p]=min(sgt[p<<1],sgt[p<<1|1]);
}
long long query(int p,int l,int r,int L,int R){
if(L>R)return 0x3f3f3f3f3f3f3fLL;
if(L<=l&&r<=R){
return sgt[p];
}
int m=(l+r)>>1;
if(R<=m)return query(p<<1,l,m,L,R);
if(m< L)return query(p<<1|1,m+1,r,L,R);
return min(query(p<<1,l,m,L,R),query(p<<1|1,m+1,r,L,R));
}
int main(){
int n;
scanf("%d",&n);
long long p;
for(int i=1;i<=n;i++){
scanf("%lld",&h[i]);
}
c[1]=0+1-h[1];
for(int i=2;i<=n;i++){
scanf("%lld",&p);
c[i]=p+i-h[i];
}
n++;
dp[n]=0;
build(1,1,n);
flow S({1e9+7,n});
for(int i=n-1;i>=1;i--){
pair<int,int> s={h[i],i};
int L=i+1,R=S.pushtill(s);
//printf("[%d,%d]",L,R);
if(R==n){
dp[i]=min(h[i]+query(1,1,n,L,R-1),(long long)n)-i;
}else{
dp[i]=h[i]-i+min(query(1,1,n,L,R-1),c[R]+2*(h[R]-h[i])+dp[R]);
}
update(1,1,n,i);
//printf("%d,%lld\n",i,dp[i]);
}
printf("%lld",dp[1]);
}