1.BZOJ4552 排序
二分答案,将大于等于答案的数置为1,否则置为0,利用线段树维护区间和模拟升序和降序操作。可以证明在操作中1的位置单调不降。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define clr(x) memset(x,0,sizeof(x))
5 #define L (x<<1)
6 #define R (x<<1|1)
7 #define mid ((l+r)>>1)
8 #define M (r-l+1)
9 #define MM (1<<17)
10 #define MN 30005
11 using namespace std;
12 inline int in(){
13 int x=0;bool f=0;char c;
14 for (;(c=getchar())<'0'||c>'9';f=c=='-');
15 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
16 return f?-x:x;
17 }
18 int sum[MM],tag[MM],a[MN],lt[MN],rt[MN];
19 int n,m,q;
20 bool b[MN],tp[MN];
21 inline void up(int x) {sum[x]=sum[L]+sum[R];}
22 inline void pushdown(int x,int l,int r){
23 if ((tag[x]<0)||l==r) return;
24 sum[L]=(M-(M>>1))*tag[x];sum[R]=(M>>1)*tag[x];
25 tag[L]=tag[R]=tag[x];tag[x]=-1;
26 }
27 inline void build(int x,int l,int r){
28 if (l==r) {sum[x]=b[l];return;}
29 build(L,l,mid);build(R,mid+1,r);up(x);
30 }
31 inline void update(int x,int l,int r,int a,int b,int v){
32 if (a<=l&&b>=r){
33 sum[x]=M*v;tag[x]=v;return;
34 }pushdown(x,l,r);
35 if (a<=mid) update(L,l,mid,a,b,v);
36 if (b>mid) update(R,mid+1,r,a,b,v);up(x);
37 }
38 inline int query(int x,int l,int r,int a,int b){
39 if (a<=l&&b>=r) return sum[x];pushdown(x,l,r);
40 if (b<=mid) return query(L,l,mid,a,b);
41 if (a>mid) return query(R,mid+1,r,a,b);
42 return query(L,l,mid,a,mid)+query(R,mid+1,r,mid+1,b);
43 }
44 inline bool check(int x){
45 memset(tag,-1,sizeof(tag));clr(sum);clr(b);
46 for (int i=1;i<=n;++i) b[i]=(a[i]>=x);build(1,1,n);
47 for (int i=1;i<=m;++i){
48 if (!tp[i]){
49 int num=query(1,1,n,lt[i],rt[i]);
50 update(1,1,n,lt[i],rt[i]-num,0);
51 update(1,1,n,rt[i]-num+1,rt[i],1);
52 }else{
53 int num=query(1,1,n,lt[i],rt[i]);
54 update(1,1,n,lt[i],lt[i]+num-1,1);
55 update(1,1,n,lt[i]+num,rt[i],0);
56 }
57 }return query(1,1,n,q,q);
58 }
59 int main()
60 {
61 n=in();m=in();
62 for (int i=1;i<=n;++i) a[i]=in();
63 for (int i=1;i<=m;++i){
64 tp[i]=in();lt[i]=in();rt[i]=in();
65 }q=in();int l=1,r=n,res=0;
66 while (l<=r){
67 if (check(mid)) res=mid,l=mid+1;
68 else r=mid-1;
69 }printf("%d",res);return 0;
70 }
2.BZOJ1084 最大子矩阵
利用列数不超过2分类讨论进行动态规划。令f[i][j][k]表示在第i行,已用j个子矩阵,情况为k时的最大子矩阵和。
注意两列均取时根据分别为两个子矩阵的元素与同时为一个子矩阵的元素分类讨论。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define inf 0x3f3f3f3f
5 #define MN 103
6 using namespace std;
7 inline int in(){
8 int x=0;bool f=0;char c;
9 for (;(c=getchar())<'0'||c>'9';f=c=='-');
10 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
11 return f?-x:x;
12 }
13 int f[2][MN][6],a[2][MN],n,m,k,res;
14 int main()
15 {
16 n=in();m=in();k=in();
17 for (int j=1;j<=n;++j)
18 for (int i=0;i<m;++i) a[i][j]=in();res=-inf;
19 if (m==1){
20 for (int i=1;i<=n;++i){
21 int I=i&1;
22 for (int j=1;j<=k;++j){
23 f[I][j][0]=max(f[I^1][j][0],f[I^1][j][1]);
24 f[I][j][1]=max(f[I^1][j][1],f[I^1][j-1][0])+a[0][i];
25 }
26 }
27 for (int j=0;j<=k;++j) res=max(res,max(f[n&1][j][0],f[n&1][j][1]));
28 }else{
29 memset(f,0xc0,sizeof(f));
30 for (int j=0;j<=k;++j) f[0][j][0]=f[1][j][0]=0;
31 for (int i=1;i<=n;++i){
32 int I=i&1;
33 for (int j=1;j<=k;++j){
34 f[I][j][0]=max(max(f[I^1][j][0],f[I^1][j][1]),max(f[I^1][j][2],max(f[I^1][j][3],f[I^1][j][4])));
35 f[I][j][1]=max(f[I^1][j][1],f[I^1][j][3])+a[0][i];
36 f[I][j][1]=max(max(f[I][j][1],f[I^1][j-1][0]+a[0][i]),max(f[I^1][j-1][2],f[I^1][j-1][4])+a[0][i]);
37 f[I][j][2]=max(f[I^1][j][2],f[I^1][j][3])+a[1][i];
38 f[I][j][2]=max(max(f[I][j][2],f[I^1][j-1][0]+a[1][i]),max(f[I^1][j-1][1],f[I^1][j-1][4])+a[1][i]);
39 f[I][j][3]=f[I^1][j][3]+a[0][i]+a[1][i];
40 f[I][j][3]=max(f[I][j][3],max(f[I^1][j-1][1],f[I^1][j-1][2])+a[0][i]+a[1][i]);
41 if (j>1) f[I][j][3]=max(f[I][j][3],max(f[I^1][j-2][0],f[I^1][j-2][4])+a[0][i]+a[1][i]);
42 f[I][j][4]=f[I^1][j][4]+a[0][i]+a[1][i];
43 f[I][j][4]=max(f[I][j][4],max(max(f[I^1][j-1][0],f[I^1][j-1][1]),max(f[I^1][j-1][2],f[I^1][j-1][3]))+a[0][i]+a[1][i]);
44 }
45 }
46 for (int j=0;j<=k;++j)
47 res=max(max(res,f[n&1][j][0]),max(max(f[n&1][j][1],f[n&1][j][2]),max(f[n&1][j][3],f[n&1][j][4])));
48 }printf("%d",res);return 0;
49 }
3.BZOJ1093 最大半连通子图
缩点,求包括所有环中点的最短路(路径上经过的点数最小)及最短路条数。在新图(DAG)中以拓扑序动态规划即可。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<set>
5 #define MM 1000005
6 #define MN 100005
7 using namespace std;
8 inline int in(){
9 int x=0;bool f=0;char c;
10 for (;(c=getchar())<'0'||c>'9';f=c=='-');
11 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
12 return f?-x:x;
13 }
14 struct st{
15 int to,nxt;
16 }e[MM],g[MM];
17 set<int> s[MN];
18 int low[MN],dfn[MN],st[MN],siz[MN],scc[MN],q[MN<<1];
19 int h[MN],l[MN],into[MN],x[MM],y[MM],d[MN],f[MN];
20 int n,m,md,mf,cnt=0,gct=0,tot=0,dfsn=0,top=0,mod;
21 bool vis[MN];
22 inline void ins(int x,int y){
23 e[++cnt].to=y;e[cnt].nxt=h[x];h[x]=cnt;
24 }
25 inline void ins2(int x,int y){
26 g[++gct].to=y;g[gct].nxt=l[x];l[x]=gct;
27 }
28 inline void tarjan(int u){
29 low[u]=dfn[u]=(++dfsn);vis[u]=1;st[++top]=u;
30 for (int i=h[u];i;i=e[i].nxt){
31 int v=e[i].to;
32 if (!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
33 else if (vis[v]) low[u]=min(low[u],dfn[v]);
34 }if (low[u]==dfn[u]){
35 int cur=0;++tot;
36 while (cur!=u){
37 cur=st[top--];vis[cur]=0;
38 scc[cur]=tot;++siz[tot];
39 }
40 }
41 }
42 int main()
43 {
44 n=in();m=in();mod=in();
45 for (int i=1;i<=m;++i){
46 x[i]=in();y[i]=in();ins(x[i],y[i]);
47 }
48 for (int i=1;i<=n;++i) if (!dfn[i]) tarjan(i);
49 for (int i=1;i<=m;++i)
50 if (scc[x[i]]!=scc[y[i]]&&(!s[scc[x[i]]].count(scc[y[i]]))){
51 s[scc[x[i]]].insert(scc[y[i]]);
52 ins2(scc[x[i]],scc[y[i]]);++into[scc[y[i]]];
53 }int hd=1,tl=0;
54 memset(vis,0,sizeof(vis));
55 for (int i=1;i<=tot;++i)
56 if (!into[i]) q[++tl]=i,d[i]=siz[i],f[i]=1;
57 while (hd<=tl){
58 int u=q[hd++];
59 for (int i=l[u];i;i=g[i].nxt){
60 int v=g[i].to;
61 if (d[v]==d[u]+siz[v]) f[v]=(f[v]+f[u])%mod;
62 else if (d[v]<d[u]+siz[v]) d[v]=d[u]+siz[v],f[v]=f[u];
63 --into[v];if (!into[v]) q[++tl]=v;
64 }
65 }
66 for (int i=1;i<=tot;++i)
67 if (md==d[i]) mf=(mf+f[i])%mod;
68 else if (md<d[i]) md=d[i],mf=f[i];
69 printf("%d
%d",md,mf);return 0;
70 }
4.BZOJ1087 互不侵犯
状态压缩动态规划。预处理降低时间复杂度。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define ll long long
5 #define MM (1<<10)
6 #define MK 103
7 #define MN 12
8 using namespace std;
9 inline int in(){
10 int x=0;bool f=0;char c;
11 for (;(c=getchar())<'0'||c>'9';f=c=='-');
12 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
13 return f?-x:x;
14 }
15 ll f[MN][MK][MM],res;
16 int num[MM],v[MM][MM+3],tl[MM];
17 int n,k;
18 bool vis[MM];
19 int main()
20 {
21 n=in();k=in();
22 for (int i=0;i<(1<<n);++i){
23 if (i&(i<<1)) continue;vis[i]=1;
24 for (int j=0;j<n;++j) num[i]+=((i>>j)&1);
25 }
26 for (int i=0;i<(1<<n);++i){
27 if (!vis[i]) continue;
28 for (int j=0;j<(1<<n);++j){
29 if (!vis[j]) continue;
30 if ((i&j)||(i&(j<<1))||((i<<1)&j)) continue;
31 v[i][++tl[i]]=j;
32 }
33 }
34 for (int i=0;i<(1<<n);++i) if (vis[i]) f[1][num[i]][i]=1ll;
35 for (int i=2;i<=n;++i)
36 for (int j=0;j<(1<<n);++j){
37 if (!vis[j]) continue;
38 for (int r=1;r<=tl[j];++r)
39 for (int l=0;l+num[j]<=k;++l) f[i][l+num[j]][j]+=f[i-1][l][v[j][r]];
40 }
41 for (int i=0;i<(1<<n);++i) if (vis[i]) res+=f[n][k][i];
42 printf("%lld",res);return 0;
43 }
5.BZOJ1202 狡猾的商人
差分约束。L-1向R连权值为v的边,R向L-1连权值为-v的边。
在原图做SPFA,判断是否有负环即可。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define clr(x) memset(x,0,sizeof(x))
5 #define MN 105
6 #define ME 2005
7 #define MM (1<<7)
8 using namespace std;
9 inline int in(){
10 int x=0;bool f=0;char c;
11 for (;(c=getchar())<'0'||c>'9';f=c=='-');
12 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
13 return f?-x:x;
14 }
15 struct edge{
16 int to,nxt,val;
17 }e[ME];
18 const int len=(1<<7)-1;
19 int h[MN],inq[MN],pre[MN],dis[MN],q[MM];
20 int n,m,l,r,v,T,cnt;
21 inline void ins(int x,int y,int v){
22 e[++cnt].to=y;e[cnt].nxt=h[x];h[x]=cnt;e[cnt].val=v;
23 }
24 inline void init(){
25 clr(e);clr(h);clr(inq);clr(pre);clr(q);cnt=0;
26 }
27 inline bool spfa(int s){
28 memset(dis,0x3f,sizeof(dis));
29 int hd=0,tl=0;
30 dis[s]=0;inq[s]=pre[s]=1;q[++tl]=s;
31 while (hd!=tl){
32 hd=(hd+1)&len;int u=q[hd];inq[u]=0;
33 for (int i=h[u];i;i=e[i].nxt){
34 int v=e[i].to;
35 if (dis[v]>dis[u]+e[i].val){
36 dis[v]=dis[u]+e[i].val;
37 if (!inq[v]){
38 inq[v]=1;++pre[v];
39 if (pre[v]>n) return 0;
40 tl=(tl+1)&len;q[tl]=v;
41 }
42 }
43 }
44 }return 1;
45 }
46 int main()
47 {
48 T=in();while (T--){
49 init();n=in();m=in();
50 for (int i=1;i<=m;++i){
51 l=in();r=in()+1;v=in();
52 ins(l,r,v);ins(r,l,-v);
53 }puts(spfa(1)?"true":"false");
54 }return 0;
55 }
6.BZOJ1801 中国象棋
可以发现,每行及每列最多只能放置2个炮,且对于每一行的放置,原有列的相对位置不影响放置的决策。
令f[i][j][k]表示做到第i行,有j行有1个炮,有k行有2个炮,分类讨论转移即可。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define mod 9999973
5 #define MN 103
6 using namespace std;
7 inline int in(){
8 int x=0;bool f=0;char c;
9 for (;(c=getchar())<'0'||c>'9';f=c=='-');
10 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
11 return f?-x:x;
12 }
13 int f[2][MN][MN],n,m,res;
14 inline int C2(int x){
15 return ((1ll*x*(x-1))>>1)%mod;
16 }
17 int main()
18 {
19 n=in();m=in();f[0][0][0]=1;
20 for (int i=1;i<=n;++i){
21 int I=i&1;
22 for (int j=0;j<=m;++j)
23 for (int k=0;j+k<=m;++k){
24 f[I][j][k]=f[I^1][j][k];
25 if (j) f[I][j][k]=(f[I][j][k]+(1ll*f[I^1][j-1][k]*(m-j-k+1))%mod)%mod;
26 if (k) f[I][j][k]=(f[I][j][k]+(1ll*f[I^1][j+1][k-1]*(j+1))%mod)%mod;
27 if (j>1) f[I][j][k]=(f[I][j][k]+(1ll*f[I^1][j-2][k]*C2(m-j-k+2))%mod)%mod;
28 if (j&&k) f[I][j][k]=(f[I][j][k]+(1ll*f[I^1][j][k-1]*(1ll*(m-j-k+1)*j)%mod)%mod)%mod;
29 if (k>1) f[I][j][k]=(f[I][j][k]+(1ll*f[I^1][j+2][k-2]*C2(j+2))%mod)%mod;
30 }
31 }
32 for (int j=0;j<=m;++j)
33 for (int k=0;j+k<=m;++k) res=(res+f[n&1][j][k])%mod;
34 printf("%d",res);return 0;
35 }
7.BZOJ2427 软件安装
Di向i连边后缩点。对于环的情况,在环上的节点必须全选或全不选。
缩点后,对于没有依赖的节点,建立虚拟源点向其连边。而后dfs做背包dp即可。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<set>
5 #define MN 103
6 #define MM 503
7 using namespace std;
8 inline int in(){
9 int x=0;bool f=0;char c;
10 for (;(c=getchar())<'0'||c>'9';f=c=='-');
11 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
12 return f?-x:x;
13 }
14 struct st{
15 int to,nxt;
16 }e[MM],g[MM];
17 set <int> s[MN];
18 int low[MN],dfn[MN],st[MN],siz[MN],scc[MN],q[MN<<1];
19 int h[MN],l[MN],into[MN],w[MN],v[MN],wt[MN],vl[MN],d[MN],f[MN][MM];
20 int n,m,cnt=0,gct=0,tot=0,dfsn=0,top=0,S;
21 bool vis[MN];
22 inline void ins(int x,int y){
23 e[++cnt].to=y;e[cnt].nxt=h[x];h[x]=cnt;
24 }
25 inline void ins2(int x,int y){
26 g[++gct].to=y;g[gct].nxt=l[x];l[x]=gct;
27 }
28 inline void tarjan(int u){
29 low[u]=dfn[u]=(++dfsn);vis[u]=1;st[++top]=u;
30 for (int i=h[u];i;i=e[i].nxt){
31 int v=e[i].to;
32 if (!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
33 else if (vis[v]) low[u]=min(low[u],dfn[v]);
34 }if (low[u]==dfn[u]){
35 int cur=0;++tot;
36 while (cur!=u){
37 cur=st[top--];vis[cur]=0;scc[cur]=tot;
38 wt[tot]+=w[cur];vl[tot]+=v[cur];
39 }
40 }
41 }
42 inline void dfs(int u){
43 for (int i=wt[u];i<=m;++i) f[u][i]=vl[u];
44 for (int i=l[u];i;i=g[i].nxt){
45 int v=g[i].to;dfs(v);
46 for (int j=m;j>=wt[u];--j)
47 for (int k=0;j-k>=wt[u];++k)
48 f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
49 }
50 }
51 int main()
52 {
53 n=in();m=in();
54 for (int i=1;i<=n;++i) w[i]=in();
55 for (int i=1;i<=n;++i) v[i]=in();
56 for (int i=1;i<=n;++i){
57 d[i]=in();if (d[i]) ins(d[i],i);
58 }
59 for (int i=1;i<=n;++i) if (!dfn[i]) tarjan(i);
60 for (int i=1;i<=n;++i)
61 if (d[i]&&scc[d[i]]!=scc[i]&&(!s[scc[d[i]]].count(scc[i]))){
62 s[scc[d[i]]].insert(scc[i]);
63 ins2(scc[d[i]],scc[i]);++into[scc[i]];
64 }S=tot+1;
65 for (int i=1;i<=tot;++i) if (!into[i]) ins2(S,i);
66 dfs(S);printf("%d",f[S][m]);return 0;
67 }
8.BZOJ1899 午餐
可以发现按照吃饭所需时间从大到小排序方案最佳。
令f[i][j]表示到第i个人,第一队等待j时间,所有前i个人都吃完饭的最小时刻。分在第一队与第二段转移。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define inf 0x3f3f3f3f
5 #define MN 203
6 using namespace std;
7 inline int in(){
8 int x=0;bool f=0;char c;
9 for (;(c=getchar())<'0'||c>'9';f=c=='-');
10 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
11 return f?-x:x;
12 }
13 struct st{
14 int a,b;
15 }p[MN];
16 inline bool cmp(st x,st y){return x.b>y.b;}
17 int sum[MN],f[2][MN*MN];
18 int n,res;
19 int main()
20 {
21 n=in();
22 for (int i=1;i<=n;++i) p[i].a=in(),p[i].b=in();
23 sort(p+1,p+n+1,cmp);
24 for (int i=1;i<=n;++i) sum[i]=sum[i-1]+p[i].a;
25 memset(f[0],0x3f,sizeof(f[0]));res=inf;f[0][0]=0;
26 for (int i=1;i<=n;++i){
27 int I=i&1;memset(f[I],0x3f,sizeof(f[I]));
28 for (int j=sum[i];j>=0;--j){
29 if (j>=p[i].a) f[I][j]=min(f[I][j],max(f[I^1][j-p[i].a],j+p[i].b));
30 f[I][j]=min(f[I][j],max(f[I^1][j],sum[i]-j+p[i].b));
31 }
32 }
33 for (int j=0;j<=sum[n];++j) res=min(res,f[n&1][j]);
34 printf("%d",res);return 0;
35 }
9.BZOJ2298 Problem A
将题目转化为在[ai+1,n-bi]覆盖一段线段,求线段总数-最多互不相交线段的段数。
将线段以右端点排序,从线段左端点处进行转移。可以删去不合法的线段并将部分线段合并。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<vector>
5 #define MN 100005
6 using namespace std;
7 inline int in(){
8 int x=0;bool f=0;char c;
9 for (;(c=getchar())<'0'||c>'9';f=c=='-');
10 for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
11 return f?-x:x;
12 }
13 struct st{
14 int a,b;
15 }c[MN];
16 vector <int> v[MN];
17 inline bool cmp(st x,st y){
18 return (x.b==y.b)?x.a<y.a:x.b<y.b;
19 }
20 int siz[MN],f[MN],n,cnt;
21 int main()
22 {
23 n=in();
24 for (int i=1;i<=n;++i){
25 c[i].a=in()+1;c[i].b=n-in();
26 }sort(c+1,c+n+1,cmp);
27 for (int i=1;i<=n;++i){
28 if (c[i].a>c[i].b) continue;
29 if (c[i].a==c[cnt].a&&c[i].b==c[cnt].b) ++siz[cnt];
30 else {
31 c[++cnt].a=c[i].a;c[cnt].b=c[i].b;
32 siz[cnt]=1,v[c[cnt].b].push_back(c[cnt].a);v[c[cnt].b].push_back(cnt);
33 }
34 }
35 for (int i=1;i<=n;++i){
36 f[i]=f[i-1];int sz=v[i].size();
37 for (int j=0;j<sz;++j) f[i]=max(f[i],f[c[v[i][j]].a-1]+min(i-c[v[i][j]].a+1,siz[v[i][j]]));
38 }
39 printf("%d",n-f[n]);return 0;
40 }