前言
这可是六一呀
JZOJ 6191 Exchange
题目
分析
首先可以预处理出一个位置的后继,这个可以通过类似于链表的结构实现,接着这就是关键了,这样貌似并没有什么意义,把这些区间按右端点从小到大排序离线询问,那只要预处理一个点的后继能散播到最靠前的那个点,那也就是这个区间可以整体交换次数加1,那么对于每个询问,输出区间中的最大值,用线段树解决
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=1500101; struct rec{int l,r,rk;}b[N];
struct segt{
......
}zkw;
int a[N],n,m,st[N],ans[N],nx[N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
bool cmp(rec x,rec y){return x.r<y.r;}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
signed main(){
n=iut(); m=iut(); a[++n]=2147483647;
for (rr int i=1;i<n;++i) a[i]=iut();
for (rr int i=n-1;i;--i){
nx[i]=i+1,st[i]=i;
while (a[i]>=a[nx[i]]) nx[i]=nx[nx[i]];
} st[n]=n;
for (rr int i=1;i<=n;++i)
st[nx[i]]=min(st[nx[i]],st[i]);;
for (rr int i=1;i<=m;++i) b[i]=(rec){iut(),iut(),i};
sort(b+1,b+1+m,cmp); for (zkw.bas=1;(zkw.bas<<=1)<n+2;);
for (rr int i=1,t=0;i<=m;++i){
while (t<b[i].r) ++t,zkw.update(st[t],t);
ans[b[i].rk]=zkw.answ(b[i].l,b[i].r);
}
for (rr int i=1;i<=m;++i) print(ans[i]),putchar(10);
return 0;
}
JZOJ 4802 探险计划
分析
就是道费用流的题目,如果点不重复走,则要拆点;能重复走,则不需要拆点,其它其实没什么好说的
代码
#include <cstdio>
#include <deque>
#include <cstring>
#define rr register
#define id(x,y) ((x-1)*m+y)
using namespace std;
const int inf=707406378;
struct node{int y,w,f,next;}e[500001]; bool v[20001];
int k=1,ls[20001],s,t,s1,cnt,ans,n,m,dis[20001],a[81][161],h[81][161];
inline void add(int x,int y,int w,int f){
e[++k]=(node){y,w,f,ls[x]}; ls[x]=k;
e[++k]=(node){x,0,-f,ls[y]}; ls[y]=k;
}
inline signed spfa(){
memset(v,0,sizeof(v)); memset(dis,127/3,sizeof(dis));
dis[t]=0; v[t]=1; rr deque<int>q; q.push_back(t);
while (q.size()){
rr int x=q.front(); q.pop_front();
for (rr int i=ls[x];i;i=e[i].next)
if (e[i^1].w&&dis[e[i].y]>dis[x]-e[i].f){
dis[e[i].y]=dis[x]-e[i].f;
if (!v[e[i].y]){
v[e[i].y]=1;
if (q.size()&&dis[e[i].y]<dis[q.front()])
q.push_front(e[i].y); else q.push_back(e[i].y);
}
}
v[x]=0;
}
return dis[s]<707406378;
}
inline signed dfs(int x,int now){
if (x==t) {v[t]=1; return now;}
rr int rest=0,f; v[x]=1;
for (rr int i=ls[x];i;i=e[i].next)
if (!v[e[i].y]&&e[i].w&&dis[e[i].y]+e[i].f==dis[x]){
rest+=(f=dfs(e[i].y,min(e[i].w,now-rest)));
if (f) ans+=f*e[i].f,e[i].w-=f,e[i^1].w+=f;
if (rest==now) break;
}
return rest;
}
inline void answ(){
while (spfa()){
v[t]=1;
while (v[t]){
memset(v,0,sizeof(v));
dfs(s,1e9);
}
}
}
signed main(){
scanf("%d%d",&n,&m); s=cnt=1;
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<m+i;++j) scanf("%d",&a[i][j]),h[i][j]=++cnt;
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<m+i;++j){
add(h[i][j],h[i][j]+cnt,1,a[i][j]);
if (i>1){
if (j<m+i-1) add(h[i-1][j]+cnt,h[i][j],1,0);
if (j>1) add(h[i-1][j-1]+cnt,h[i][j],1,0);
}else add(s,h[i][j],1,0);
} t=cnt+1,s1=t+1;
for (rr int i=1;i<m+n;++i) add(h[n][i]+cnt,t,1,0);
answ(); printf("%d\n",ans);
memset(ls,0,sizeof(ls)); k=1; add(s,s1,m,0);
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<m+i;++j){
if (i>1){
if (j<m+i-1) add(h[i-1][j],h[i][j],1,a[i][j]);
if (j>1) add(h[i-1][j-1],h[i][j],1,a[i][j]);
}else add(s1,h[i][j],inf,a[i][j]);
}
for (rr int i=1;i<m+n;++i) add(h[n][i],t,inf,0);
ans=0; answ();
return !printf("%d",ans);
}
JZOJ 4800 周末晚会
题目
n n n个人围绕着圆桌坐着,其中一些是男孩,另一些是女孩。你的任务是找出所有合法的方案数,使得不超过 k k k个女孩座位是连续的。
分析
首先如果没有圆桌,设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示前
i
i
i个人,末尾
j
j
j个是女生的合法方案数,
那么
d
p
[
i
]
[
0
]
=
∑
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
(
j
≠
0
)
dp[i][0]=\sum dp[i-1][j],dp[i][j]=dp[i-1][j-1](j\neq 0)
dp[i][0]=∑dp[i−1][j],dp[i][j]=dp[i−1][j−1](j̸=0),特殊地,
d
p
[
1
]
[
1
]
=
1
dp[1][1]=1
dp[1][1]=1,若
n
≤
k
n\leq k
n≤k再把它加上,因为若第一个是女生会在后面的计算中产生错误,所以要特殊处理
之后设
c
[
i
]
c[i]
c[i]表示前
i
i
i个人放在圆桌不考虑重复的合法方案数,那么
c
[
i
]
=
∑
j
=
0
k
d
p
[
i
]
[
j
]
∗
(
j
+
1
)
c[i]=\sum_{j=0}^kdp[i][j]*(j+1)
c[i]=∑j=0kdp[i][j]∗(j+1)
再设
p
[
i
]
p[i]
p[i]表示前
i
i
i个人在
c
[
i
]
c[i]
c[i]的前提下没有循环节的合法方案数,那么
p
[
i
]
=
c
[
i
]
−
∑
j
∣
i
,
j
≠
i
c
[
j
]
p[i]=c[i]-\sum_{j|i,j\neq i}c[j]
p[i]=c[i]−∑j∣i,j̸=ic[j]
那么答案就是
∑
d
∣
n
p
[
d
]
d
\sum_{d|n}\frac{p[d]}{d}
d∣n∑dp[d]
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
const int mod=1e8+7;
int inv[2001],dp[2001][2001],f[2001],s[2001];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed mo(int x){return x>=mod?x-mod:x;}
signed main(){
inv[0]=inv[1]=1;
for (rr int i=2;i<2001;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for (rr int t=iut();t;--t){
rr int n=iut(),k=iut();
memset(dp,0,sizeof(dp)); dp[1][0]=1;
for (rr int i=1;i<n;++i)
for (rr int j=i-1<k?i-1:k;~j;--j)
dp[i+1][0]=mo(dp[i+1][0]+dp[i][j]),dp[i+1][j+1]=dp[i][j];
memset(f,0,sizeof(f));
for (rr int i=1;i<=n;++i)
for (rr int j=0;j<=k;++j)
f[i]=mo(f[i]+1ll*dp[i][j]*(j+1)%mod);
for (rr int i=1;i<=n;++i){
s[i]=mo(f[i]-s[1]*(i>1)+mod);
for (rr int j=2;j*j<=i;++j)
if (!(i%j)){
s[i]=mo(s[i]-s[j]+mod);
if (j*j<i) s[i]=mo(s[i]-s[i/j]+mod);
}
}
rr int ans=n<=k;
for (rr int i=1;i*i<=n;++i)
if (!(n%i)){
ans=mo(ans+1ll*s[i]*inv[i]%mod);
if (i*i<n) ans=mo(ans+1ll*s[n/i]*inv[n/i]%mod);
}
printf("%d\n",ans);
}
return 0;
}
后续
啊啊啊,好难啊