@Plutorabbit
2017-02-19T09:56:46.000000Z
字数 7168
阅读 2079
POI
2016
OI
BZOJ
BZOJ-4345 ~ BZOJ-4348
先用优先队列构造出第k小的权值,然后DFS求出方案。
用线段树找比t小的最前面的元素
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int read()
{
int x=0; bool f=0; char ch;
for (ch=getchar();ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; if (f) x=-x;
return x;
}
const int NN = 1000005;
int n,m,cnt,top,a[NN],num[NN],mn[NN<<2],st[NN];
LL ans[NN];
struct Q
{
LL x; int y;
}tmp;
bool operator < (Q xx,Q yy) { return xx.x > yy.x; }
priority_queue<Q> que;
void Build(int k,int l,int r)
{
if (l == r) { mn[k] = a[l]; return; }
int mid = (l+r) >> 1;
Build(k<<1,l,mid), Build(k<<1|1,mid+1,r);
mn[k] = min(mn[k<<1],mn[k<<1|1]);
}
int Query(int k,int l,int r,int x,LL y)
{
if (x <= l)
{
if (mn[k] > y) return 0;
if (l == r) return l;
}
int mid = (l+r) >> 1,tmpp;
if (x <= mid) if ((tmpp=Query(k<<1,l,mid,x,y))) return tmpp;
return Query(k<<1|1,mid+1,r,x,y);
}
void DFS(int xx,LL res)
{
if (!cnt) return;
if (!res)
{
--cnt;
if (!cnt) for (int i=1;i<=top;++i) printf("%d ",st[i]);
return;
}
for (int i=xx+1;i<=n;++i)
{
i = Query(1,1,n,i,res);
if (i) st[++top] = i, DFS(i,res-a[i]), --top; else break;
}
}
int main()
{
n = read(), m = read()-1;
if (m == 0) { puts("0"); return 0; }
for (int i=1;i<=n;++i) a[i] = num[i] = read();
sort(num+1,num+1+n);
tmp.x = num[1], tmp.y = 1;
que.push(tmp);
for (int i=1;i<=m;++i)
{
tmp = que.top(); que.pop();
ans[i] = tmp.x;
if (i < m && tmp.y < n)
{
tmp.y ++, tmp.x += num[tmp.y], que.push(tmp);
tmp.x -= num[tmp.y-1], que.push(tmp);
}
}
for (int i=m;i&&ans[i]==ans[m];--i) ++cnt;
printf("%lld\n",ans[m]);
Build(1,1,n);
DFS(0,ans[m]);
return 0;
}
树型动规看出来应该很简单吧QAQ
分两种情况。
1. 在这个点放wifi。那么可以用h[i][1],h[i][2]表示放1、2个wifi的最优解,用儿子的对应状态转移一下即可。
2. 不放wifi。此时i到儿子的路径有两种情况:被i的儿子覆盖;被i的儿子的儿子和i的父亲覆盖。令f[i][x][y]表示i不放的时候,父亲节点放x个,儿子节点放y个。转移起来比较复杂,把9种状态压缩一下做。
#include <bits/stdc++.h>
using namespace std;
int read()
{
int x=0; bool f=0; char ch;
for (ch=getchar();ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; if (f) x=-x;
return x;
}
const int INF = 0x3f3f3f3f,NN = 200005;
int n,ans,tot=0,head[NN],f[NN][3][3],g[NN][3],h[NN][3],q[NN][3],t[3][3];
struct E
{
int ne,v;
}e[NN<<1];
void Build(int xx,int yy)
{
e[++tot].ne = head[xx], head[xx] = tot, e[tot].v = yy;
e[++tot].ne = head[yy], head[yy] = tot, e[tot].v = xx;
}
void DFS(int xx,int fa)
{
h[xx][1] = 1, h[xx][2] = 2;
memset(g[xx],0x3f,sizeof(g[xx])), memset(f[xx],0x3f,sizeof(f[xx]));
f[xx][0][0] = 0;
for (int i=head[xx];i;i=e[i].ne)
if (e[i].v != fa)
{
DFS(e[i].v,xx);
int v = e[i].v, tmp = min(min(h[v][1],h[v][2]),q[v][1]);
for (int i=0;i<3;++i) tmp = min(tmp,g[v][i]);
h[xx][1] += tmp, h[xx][2] += min(tmp,q[v][2]);
memset(t,0x3f,sizeof(t));
for (int i=0;i<3;++i)
for (int j=0;j<3;++j)
if (f[xx][i][j] < INF)
{
tmp = f[xx][i][j];
for (int k=1;k<3;++k) t[min(2,i+k)][j] = min(t[min(2,i+k)][j],tmp+h[v][k]);
for (int k=0;k<3;++k) t[i][max(2-k,j)] = min(t[i][max(2-k,j)],tmp+g[v][k]);
}
memcpy(f[xx],t,sizeof(t));
}
for (int i=0;i<3;++i)
for (int j=0;j<=i;++j) g[xx][i] = min(g[xx][i],f[xx][i][j]);
q[xx][1] = min(f[xx][0][1],f[xx][1][2]), q[xx][2] = f[xx][0][2];
}
int main()
{
n = read();
int x,y;
for (int i=1;i<n;++i) x = read(), y = read(), Build(x,y);
DFS(1,0);
ans = min(h[1][1],h[1][2]);
for (int i=0;i<3;++i) ans = min(ans,g[1][i]);
printf("%d\n",ans);
return 0;
}
nim游戏变种,其实就是要求有多少种d为个数约数,剩余异或和为0的方案。
因此我们可以排序,由于x异或之后一定转移到2x内的数,所以说复杂度
#include <bits/stdc++.h>
using namespace std;
int read()
{
int x=0; bool f=0; char ch;
for (ch=getchar();ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; if (f) x=-x;
return x;
}
const int mod = 1000000007, NN = (1<<20);
int n,m,d,f[10][NN],c[NN],g[NN];
int main()
{
n = read(), d = read();
f[0][0] = 1;
int x,l,t;
for (int i=1;i<=n;++i) x = read(), ++c[x], m = max(m,x);
for (int k=1;k<=m;++k)
while (c[k]--)
{
for (l=1;l<=k;l<<=1);
for (int i=0;i<l;++i) g[i] = (f[d-1][i] + f[0][i^k]) % mod;
for (int i=d-1;i;--i)
for (int j=0;j<l;++j)
{
if (j > (j^k)) continue;
t = f[i][j];
f[i][j] = (f[i-1][j] + f[i][j^k]) % mod;
f[i][j^k] = (f[i-1][j^k] + t) % mod;
}
for (int i=0;i<l;++i) f[0][i] = g[i];
}
if (n % d == 0) f[0][0] = (f[0][0] - 1 + mod) % mod;
printf("%d\n",f[0][0]);
return 0;
}
首先特判全部都是A或者全部都是B或者的情况。
然后把矩阵四周都填充上A,枚举一个块,分情况讨论:
1.暴力枚举四周两个块;
2.一个方向上在四周扩展两步;
3.一个角落上斜着扩展一步,再扩展另一步。
复杂度
感觉自己写得要死要活的了TAT
#include <bits/stdc++.h>
using namespace std;
int read()
{
int x=0; bool f=0; char ch;
for (ch=getchar();ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; if (f) x=-x;
return x;
}
const int NN = 1005;
int n,m,tot,w,a[NN][NN],f[NN][NN],q[10005][3],ans;
char ch[NN];
struct S
{
int x,y,l,r,s;
}s[NN*NN];
void UP(int xx,int yy,int zz){ q[++tot][0]=xx; q[tot][1]=yy; q[tot][2]=zz; }
int SZ(int xx) { return s[xx].s; }
void M(int xx) { w = max(w,xx); }
void Get(int xx,int yy,int zz)
{
s[zz].s ++, f[xx][yy] = zz, s[zz].y = max(s[zz].y,xx), s[zz].r = max(s[zz].r,yy);
if (a[xx+1][yy] && !f[xx+1][yy]) Get(xx+1,yy,zz);
if (a[xx][yy+1] && !f[xx][yy+1]) Get(xx,yy+1,zz);
}
int main()
{
n = read();
if (n == 1) { puts("1"); return 0; }
for (int i=1,j;i<=n;++i)
for (scanf("%s",ch+1),j=1;j<=n;++j) a[i][j] = (ch[j]=='B');
int x,y,l,r,tmp;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
if (a[i][j] && !f[i][j]) s[++m].x = i, s[m].l = j, Get(i,j,m);
if (m == 0) { puts("2"); return 0; }
if (m == 1) { printf("%d\n",min(n*n,s[1].s+2)); return 0; }
for (int i=1;i<=m;++i)
{
w = tot = 0;
x = s[i].x, y = s[i].y, l = s[i].l, r = s[i].r;
for (int j=x;j<=y;++j)
{
if (l > 2)
{
if (a[j][l-2]) UP(f[j][l-2],0,0);
else M(SZ(f[j][l-3])+(SZ(f[j-1][l-2])|SZ(f[j-1][l-1]))+(SZ(f[j+1][l-2])|SZ(f[j+1][l-1])));
}
if (r < n-1)
{
if (a[j][r+2]) UP(f[j][r+2],0,0);
else M(SZ(f[j][r+3])+(SZ(f[j-1][r+2])|SZ(f[j-1][r+1]))+(SZ(f[j+1][r+2])|SZ(f[j+1][r+1])));
}
}
for (int j=l;j<=r;++j)
{
if (x > 2)
{
if (a[x-2][j]) UP(f[x-2][j],0,0);
else M(SZ(f[x-3][j])+(SZ(f[x-2][j-1])|SZ(f[x-1][j-1]))+(SZ(f[x-2][j+1])|SZ(f[x-1][j+1])));
}
if (y < n-1)
{
if (a[y+2][j]) UP(f[y+2][j],0,0);
else M(SZ(f[y+3][j])+(SZ(f[y+2][j-1])|SZ(f[y+1][j-1]))+(SZ(f[y+2][j+1])|SZ(f[y+1][j+1])));
}
}
if (a[x-1][l-1])
{
if (x>1) UP(f[x-1][l-1],f[x-2][l],0);
if (l>1) UP(f[x-1][l-1],f[x][l-2],0);
}
if (a[y+1][l-1])
{
if (y<n) UP(f[y+1][l-1],f[y+2][l],0);
if (l>1) UP(f[y+1][l-1],f[y][l-2],0);
}
if (a[x-1][r+1])
{
if (x>1) UP(f[x-1][r+1],f[x-2][r],0);
if (r<n) UP(f[x-1][r+1],f[x][r+2],0);
}
if (a[y+1][r+1])
{
if (y<n) UP(f[y+1][r+1],f[y+2][r],0);
if (r<n) UP(f[y+1][r+1],f[y][r+2],0);
}
if (l == r)
{
if (x>1) UP(f[x-2][l],f[x-1][l-1],f[x-1][l+1]);
if (y<n) UP(f[y+2][l],f[y+1][l-1],f[y+1][l+1]);
}
if (x == y)
{
if (l>1) UP(f[x][l-2],f[x-1][l-1],f[x+1][l-1]);
if (r<n) UP(f[x][r+2],f[x-1][r+1],f[x+1][r+1]);
}
for (int j=1;j<=tot;++j)
for (int k=j+1;k<=tot;++k)
{
tmp = SZ(q[j][0]) + SZ(q[j][1]) + SZ(q[j][2]);
if (q[j][0] != q[k][0] && q[j][1] != q[k][0] && q[j][2] != q[k][0]) tmp += SZ(q[k][0]);
if (q[j][0] != q[k][1] && q[j][1] != q[k][1] && q[j][2] != q[k][1]) tmp += SZ(q[k][1]);
if (q[j][0] != q[k][2] && q[j][1] != q[k][2] && q[j][2] != q[k][2]) tmp += SZ(q[k][2]);
M(tmp);
}
if (x>1 && l>1 && !a[x-1][l-1])
{
M(SZ(f[x-1][l-2])+(SZ(f[x-2][l])|SZ(f[x-2][l-1]))+(l==r?SZ(f[x-1][l+1]):0));
M(SZ(f[x-2][l-1])+(SZ(f[x][l-2])|SZ(f[x-1][l-2]))+(x==y?SZ(f[x+1][l-1]):0));
}
if (x>1 && r<n && !a[x-1][r+1])
{
M(SZ(f[x-1][r+2])+(SZ(f[x-2][r])|SZ(f[x-2][r+1]))+(l==r?SZ(f[x-1][r-1]):0));
M(SZ(f[x-2][r+1])+(SZ(f[x][r+2])|SZ(f[x-1][r+2]))+(x==y?SZ(f[x+1][r+1]):0));
}
if (y<n && l>1 && !a[y+1][l-1])
{
M(SZ(f[y+1][l-2])+(SZ(f[y+2][l])|SZ(f[y+2][l-1]))+(l==r?SZ(f[y+1][l+1]):0));
M(SZ(f[y+2][l-1])+(SZ(f[y][l-2])|SZ(f[y+1][l-2]))+(x==y?SZ(f[y-1][l-1]):0));
}
if (y<n && r<n && !a[y+1][r+1])
{
M(SZ(f[y+1][r+2])+(SZ(f[y+2][r])|SZ(f[y+2][r+1]))+(l==r?SZ(f[y+1][r-1]):0));
M(SZ(f[y+2][r+1])+(SZ(f[y][r+2])|SZ(f[y+1][r+2]))+(x==y?SZ(f[y-1][r+1]):0));
}
ans = max(ans,w+SZ(i)+2);
}
printf("%d\n",ans);
return 0;
}