一双木棋
我们把分割已选棋子和未选棋子的轮廓线状压一下就可以了。
代码:
include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=1<<20,inf=0x3f3f3f3f;
int n,m,a[15][15],b[15][15],f[N];
inline int dfs(int sta,bool turn){
if(~f[sta])return f[sta];
f[sta]=turn?-inf:inf;
for(ri nsta,i=0,x=n,y=0;i<n+m-1;++i){
(sta>>i)&1?--x:++y;
if(((sta>>i)&3)^1)continue;
nsta=sta^(3<<i);
f[sta]=turn?max(f[sta],dfs(nsta,turn^1)+a[x][y]):min(f[sta],dfs(nsta,turn^1)-b[x][y]);
}
return f[sta];
}
int main(){
n=read(),m=read(),memset(f,-1,sizeof(f));
for(ri i=0;i<n;++i)for(ri j=0;j<m;++j)a[i][j]=read();
for(ri i=0;i<n;++i)for(ri j=0;j<m;++j)b[i][j]=read();
f[((1<<n)-1)<<m]=0;
cout<<dfs((1<<n)-1,1);
return 0;
}
IIIDX
显然可以把他们的限制关系看成边,于是所有点构成了一棵树。
有一个显然的 60 60 60分的按 s i z e size size贪心:每次处理到当前树节点的时候预留出子树大小那么多位置然后填上这个数,但这只能处理没有相同数的情况。
现在考虑有相同数的时候怎么办?我们用线段树来模拟如下操作:每次预留出应该放的,然后取当前可以取的最大的,到了自己的第一个儿子的时候把预留的位置退掉即可,详见代码。
代码:
#include
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=5e5+5;
int n,a[N],fa[N],siz[N],cnt[N],pos[N];
double k;
namespace SGT{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
int mn[N<<2],add[N<<2];
inline void pushup(int p){mn[p]=min(mn[lc],mn[rc]);}
inline void pushnow(int p,int v){add[p]+=v,mn[p]+=v;}
inline void pushdown(int p){if(add[p])pushnow(lc,add[p]),pushnow(rc,add[p]),add[p]=0;}
inline void build(int p,int l,int r){
if(l==r){mn[p]=l;return;}
build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int l,int r,int ql,int qr,int v){
if(ql<=l&&r<=qr)return pushnow(p,v);
pushdown(p);
if(qr<=mid)update(lc,l,mid,ql,qr,v);
else if(ql>mid)update(rc,mid+1,r,ql,qr,v);
else update(lc,l,mid,ql,mid,v),update(rc,mid+1,r,mid+1,qr,v);
pushup(p);
}
inline int query(int p,int l,int r,int k){
if(l==r)return mn[p]>=k?l:l+1;
pushdown(p);
if(mn[rc]>=k)return query(lc,l,mid,k);
return query(rc,mid+1,r,k);
}
#undef lc
#undef rc
#undef mid
}
inline bool cmp(const int&a,const int&b){return a>b;}
int main(){
n=read(),scanf("%lf",&k);
for(ri i=1;i<=n;++i)a[i]=read(),fa[i]=(int)floor(i/k),siz[i]=1;
for(ri i=n;i^1;--i)siz[fa[i]]+=siz[i];
sort(a+1,a+n+1,cmp);
cnt[n]=n;
for(ri i=n-1;i;--i)cnt[i]=a[i]==a[i+1]?cnt[i+1]:i;
SGT::build(1,1,n);
for(ri p,i=1;i<=n;++i){
if(fa[i]&&(fa[i]^fa[i-1]))SGT::update(1,1,n,pos[fa[i]],n,siz[fa[i]]-1);
p=cnt[SGT::query(1,1,n,siz[i])];
++cnt[p],cout<<a[pos[i]=p]<<' ';
SGT::update(1,1,n,p,n,-siz[i]);
}
return 0;
}
秘密袭击
博主 t c l tcl tcl了并不想写毫无人性可言的正解,考虑暴力艹标算。
我们枚举每个点作为根且作为答案,这样可以把树上所有点的点权变成 01 01 01点权,然后做树上背包即可。
代码:
#include
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=4005,mod=64123;
int f[N][N],n,k,w,a[N],ans=0,rt;
vector<int>e[N];
typedef long long ll;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void dfs(int p,int fa){
if(a[p]>a[rt]||(a[p]==a[rt]&&p>rt))for(ri i=1;i<=k;++i)f[p][i]=f[fa][i-1];
else for(ri i=1;i<=k;++i)f[p][i]=f[fa][i];
for(ri i=0;i<e[p].size();++i)if(fa^e[p][i])dfs(e[p][i],p);
for(ri i=1;i<=k;++i)f[fa][i]=add(f[fa][i],f[p][i]);
}
inline void solve(int Rt){
int siz=1;
for(ri i=1;i<=n;++i)if(a[i]>a[Rt]||(a[i]==a[Rt]&&i>Rt))++siz;
if(siz<k)return;
rt=Rt;
for(ri i=2;i<=k;++i)f[rt][i]=0;
f[rt][1]=1;
for(ri i=0;i<e[rt].size();++i)dfs(e[rt][i],rt);
ans=add(ans,mul(a[rt],f[rt][k]));
}
int main(){
n=read(),k=read(),w=read();
for(ri i=1;i<=n;++i)a[i]=read();
for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].emplace_back(v),e[v].emplace_back(u);
for(ri i=1;i<=n;++i)solve(i);
cout<<ans;
return 0;
}
劈配
第一个问题解法:
每次从一个人的第一志愿开始加边,每加一次边跑一次最大流看能不能增广,不行就删掉这次加的边,可以就更新答案退出。
第二个问题解法:
对于每个人二分一下到哪个位置合适,这个时候发现边的情况很窒息,于是我们记一个 g i g_i gi表示前 i i i个人的边加入后的图长什么样,这样就可以很快的 c h e c k check check了。
代码:
#include
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=205,M=10005,inf=0x3f3f3f3f;
int n,m,b[N],a[N][N],s[N],ans[N],Ans[N];
vector<int>id[N][N];
struct edge{int v,next,c;};
struct Dinic{
edge e[M<<1];
int first[M],cur[M],cnt,d[M],q[M],hd,tl,s,t;
inline void init(){memset(first,-1,sizeof(first)),cnt=-1,s=0,t=n+m+1;}
inline void addedge(int u,int v,int c){e[++cnt]=(edge){v,first[u],c},first[u]=cnt;}
inline void add(int u,int v,int c){addedge(u,v,c),addedge(v,u,0);}
inline void del(int u,int v){first[u]=e[first[u]].next,first[v]=e[first[v]].next,cnt-=2;}
inline bool bfs(){
for(ri i=s;i<=t;++i)d[i]=-1;
q[hd=tl=1]=s,d[s]=0;
while(hd<=tl){
int x=q[hd++];
for(ri i=first[x],v;~i;i=e[i].next){
if(~d[v=e[i].v]||!e[i].c)continue;
d[v]=d[x]+1,q[++tl]=v;
if(v==t)return 1;
}
}
return 0;
}
inline int dfs(int x,int f){
if(x==t||!f)return f;
int flow=f;
for(ri&i=cur[x],v,tmp;~i;i=e[i].next){
if(!flow)return f;
if(e[i].c&&d[v=e[i].v]==d[x]+1){
tmp=dfs(v,min(e[i].c,flow));
if(!tmp)d[v]=-1;
flow-=tmp,e[i].c-=tmp,e[i^1].c+=tmp;
}
}
return f-flow;
}
}g[N];
inline void solve1(){
for(ri i=1;i<=n;++i){
g[i]=g[i-1],g[i].add(g[i].s,i,1);
for(ri j=1;j<=m;++j){
for(ri k=0;k<id[i][j].size();++k)g[i].add(i,id[i][j][k]+n,1);
if(g[i].bfs()){memcpy(g[i].cur,g[i].first,sizeof(int)*(g[i].t+1)),g[i].dfs(g[i].s,inf),ans[i]=j;break;}
for(ri k=0;k<id[i][j].size();++k)g[i].del(i,id[i][j][k]+n);
}
}
for(ri i=1;i<=n;++i)cout<<(ans[i]=ans[i]?ans[i]:m+1,ans[i])<<' ';
puts("");
}
inline bool check(int u,int tim){
Dinic tmp=g[tim-1];
tmp.add(tmp.s,u,1);
for(ri i=1;i<=s[u];++i)for(ri j=0;j<id[u][i].size();++j)tmp.add(u,id[u][i][j]+n,1);
return tmp.bfs();
}
inline void solve2(){
for(ri tmp,i=1,l,r;i<=n;++i){
if(ans[i]<=s[i])continue;
tmp=inf,l=1,r=i-1;
while(l<=r){
int mid=l+r>>1;
if(check(i,mid))l=mid+1,tmp=mid;
else r=mid-1;
}
Ans[i]=tmp^inf?i-tmp:i;
}
for(ri i=1;i<=n;++i)cout<<Ans[i]<<' ';
puts("");
}
int main(){
for(ri tt=read(),xxx=read();tt;--tt){
n=read(),m=read(),memset(ans,0,sizeof(ans)),memset(Ans,0,sizeof(Ans));
g[0].init();
for(ri i=1;i<=m;++i)b[i]=read(),g[0].add(i+n,g[0].t,b[i]);
for(ri i=1;i<=n;++i)for(ri j=1;j<=m;++j)id[i][j].clear();
for(ri i=1;i<=n;++i)for(ri j=1;j<=m;++j){
a[i][j]=read();
if(a[i][j])id[i][a[i][j]].push_back(j);
}
for(ri i=1;i<=n;++i)s[i]=read();
solve1();
solve2();
}
return 0;
}
林克卡特树
先带权二分,然后上树形 d p dp dp来进行 c h e c k check check。
树形 d p dp dp的状态表示: f i , j f_{i,j} fi,j表示以 i i i为根的子树里面选若干条不相交的链, i i i的度数为 j j j时的最优值,分情况转一下即可。
代码:
#include
#define ri register int
#define int long long
#define fi first
#define se second
using namespace std;
inline int read(){
int ans=0;
bool f=1;
char ch=getchar();
while(!isdigit(ch))f^=ch=='-',ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return f?ans:-ans;
}
typedef long long ll;
typedef pair<int,ll> pii;
const int N=3e5+5,inf=0x3f3f3f3f;
const ll Inf=0x3f3f3f3f3f3f3f;
vector<pii>e[N];
ll det;
int n,k;
struct data{
ll w;
int v;
friend inline data operator+(const data&a,const data&b){return (data){a.w+b.w,a.v+b.v};}
friend inline bool operator<(const data&a,const data&b){return a.w==b.w?a.v<b.v:a.w<b.w;}
}f[N][3];
inline data max(const data&a,const data&b){return a<b?b:a;}
inline data max(const data&a,const data&b,const data&c){return a<b?(b<c?c:b):(a<c?c:a);}
void dfs(int p,int fa){
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i].fi)==fa)continue;
dfs(v,p);
f[p][2]=max(f[p][2]+f[v][0],f[p][1]+f[v][1]+(data){e[p][i].se-det,1});
f[p][1]=max(f[p][1]+f[v][0],f[p][0]+f[v][1]+(data){e[p][i].se,0});
f[p][0]=f[p][0]+f[v][0];
}
f[p][0]=max(f[p][0],f[p][2],f[p][1]+(data){-det,1});
}
inline ll check(ll x,bool flag){
det=x;
for(ri i=1;i<=n;++i)f[i][0]=(data){0,0},f[i][1]=(data){0,0},f[i][2]=(data){-det,1};
dfs(1,0);
if(!flag)return f[1][0].v>=k;
return f[1][0].w+x*k;
}
signed main(){
n=read(),k=read()+1;
for(ri i=1,u,v,w;i<n;++i){
u=read(),v=read(),w=read();
e[u].push_back(pii(v,w));
e[v].push_back(pii(u,w));
}
ll l=-1e15,r=1e15,ans=-1e15;
while(l<=r){
ll mid=l+r>>1;
if(check(mid,0))l=mid+1,ans=mid;
else r=mid-1;
}
cout<<check(ans,1);
exit(0);
}
制胡窜
咕咕咕