T1:
题目大意:给你一张图,每条边有边权,要求保留边权和尽量大的边使得每个点至多有一条出边和至多一条入边,点数200,边数5000
解法:显然的二分图最小费用流,可行流即可不需要最大流
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=1e6+5,INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f;
int vis[N<<1],head[N],nxt[N<<1],c[N<<1],e[N<<1],tot=1;
inline void add(int x,int y,int z,int w){
vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;e[tot]=w;
vis[++tot]=x;nxt[tot]=head[y];head[y]=tot;c[tot]=0;e[tot]=-w;
}
ll d[N];
int pt[N];
int s,t;
inline bool spfa(){
queue<int>q;q.push(s);
for(int i=1;i<=t;i++) d[i]=inf;
d[s]=0;
while(!q.empty()){
int x=q.front();q.pop();pt[x]=0;
for(int i=head[x];i;i=nxt[i]){
int y=vis[i];
if(c[i]>0 && d[y]>d[x]+e[i]){
d[y]=d[x]+e[i];
if(!pt[y]){q.push(y);pt[y]=1;}
}
}
}
return d[t]!=inf;
}
int mxflow=0;ll mncost=0;
int cur[N];
ll ans=INF;
inline int dfs(int v,int flow){
if(v==t){mxflow+=flow;return flow;}
int res=0;pt[v]=1;
for(int i=cur[v];i;i=nxt[i]){
int y=vis[i];
if(!pt[y] && d[y]==d[v]+e[i] && c[i]){
cur[v]=i;
int k=dfs(y,min(flow-res,c[i]));
c[i]-=k;c[i^1]+=k;res+=k;mncost+=1ll*k*e[i];
if(res==flow) break;
}
}
pt[v]=0;
return res;
}
inline void mcmf(){while(spfa()) memcpy(cur,head,sizeof(head)),dfs(s,INF),ans=min(ans,mncost);}
ll sum=0;
signed main(){
int n=read(),m=read();
for(int x,y,i=1;i<=m;i++){
x=read(),y=read();ll z=read();
sum+=z;if(z<=0) continue;
add(x,y+n,1,-z);
}
s=0,t=n+n+1;
for(int i=1;i<=n;i++) add(s,i,1,0);
for(int i=1;i<=n;i++) add(i+n,t,1,0);
mcmf();cout<<sum+ans;
return 0;
}
T2:给你一个最大50x50的矩阵,每个点有一个要求的颜色,或者这个点禁止粉刷,现在可以用任意颜色粉刷任意一行或列,求每个点粉刷成要求颜色的最少次数并输出方案
解法:显然最多粉刷n+m-1次,那就是有一行没有被刷到(或者列,可以通过旋转矩阵再做一遍处理),枚举哪一行没被刷到,然后可以通过这一行的格子要求的颜色分析出某些列刷成了什么颜色,然后这些列上与这个格子的颜色不同的就是后来又被刷了一次,这样可以建出数个限制,如果有环显然无解,所以拓扑排序即可
Code:
#include<bits/stdc++.h>
#define pb push_back
#define db double
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
namespace topsort{
const int N=205,M=40005;
int vis[M<<1],head[N],nxt[M<<1],tot=0;
int in[N],pt[N];
vector<int>rk;
inline void clear(){memset(head,0,sizeof(head));tot=0;memset(in,0,sizeof(in));}
inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;++in[y];}
inline bool topsort(int n){
queue<int>q;rk.clear();
for(int i=1;i<=n;i++) if(pt[i] && !in[i]) q.push(i);
while(!q.empty()){
int x=q.front();q.pop();rk.pb(x);
for(int i=head[x];i;i=nxt[i]){
int y=vis[i];--in[y];
if(!in[y]) q.push(y);
}
}
for(int i=1;i<=n;i++) if(in[i]) return 0;
return 1;
}
}
using namespace topsort;
const int NN=55;
int col[NN][NN],tmp[NN][NN];
int n,m,c;
struct info{
int op,num,col;
info(){}
info(int _op,int _num,int _col):op(_op),num(_num),col(_col){}
};
vector<info>ans,now;
int R[NN],C[NN],flag=0;
inline bool check(int x){
for(int i=1;i<=m;i++) C[i]=col[x][i];
for(int i=1;i<=n;i++) R[i]=-1;
R[x]=0;clear();
for(int i=1;i<=n;i++){
if(x==i) continue;
for(int j=1;j<=m;j++) if(C[j]!=col[i][j]){
if(C[j]!=0 && col[i][j]==0) return false;
if(R[i]==-1) R[i]=col[i][j];
else if(R[i]!=col[i][j]) return false;
}
if(R[i]==-1) R[i]=0;
if(R[i]==0) continue;
for(int j=1;j<=m;j++){
if(C[j]==0) continue;
if(C[j]!=col[i][j]) add(j+n,i);
else if(C[j]==col[i][j] && C[j]!=R[i]) add(i,j+n);
}
}
for(int i=1;i<=n;i++) pt[i]=R[i]!=0;
for(int i=1;i<=m;i++) pt[n+i]=C[i]!=0;
if(!topsort::topsort(n+m)) return false;
now.clear();
for(int i=0;i<rk.size();i++){
if(rk[i]<=n) now.pb(info(0,rk[i],R[rk[i]]));
else now.pb(info(1,rk[i]-n,C[rk[i]-n]));
}
return true;
}
inline void file(){freopen("airline.in","r",stdin);freopen("airline.out","w",stdout);}
int main(){
n=read();m=read();c=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) col[i][j]=read();
for(int i=1;i<=n;i++) if(check(i) && (!flag || ans.size()>now.size())) ans=now,flag=1;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) tmp[j][i]=col[i][j];
for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) col[i][j]=tmp[i][j];
swap(n,m);
for(int i=1;i<=n;i++) if(check(i) && (!flag || ans.size()>now.size())) ans=now,flag=2;
if(flag){
if(flag==2) for(int i=0;i<ans.size();i++) ans[i].op^=1;
cout<<ans.size()<<"\n";
for(int i=0;i<ans.size();i++) cout<<(ans[i].op?"C":"R")<<" "<<ans[i].num<<" "<<ans[i].col<<"\n";
}
else puts("-1");
return 0;
}
T3:有一棵树,1为根,初始每个叶子结点被占领,每个被占领的点每秒会产出一个士兵,士兵会一直往根节点走,走到根节点后自动消失,走到一个没有被占领的节点时会对其造成1点损伤,当一个没被占领的节点受到等同于其防御值的伤害后会被占领,求所有节点最晚多久被全部占领,规模100000
解法:考虑
d
p
dp
dp,
d
p
[
i
]
dp[i]
dp[i]表示
i
i
i点被占领的时间,则
i
i
i点的值由其子树中所有点转移而来,具体地,把子树所有点的dp值排序,则排序后两个相邻时刻之差乘上对应的时刻数就是这一段时间造成的伤害值,那么求个前缀和就可以转移
则我们需要一个数据结构支持:
1.排序
2.二分查询前缀和
3.合并
显然是平衡树,splay启发式合并即可,也可以用dsu on tree
Code:
#include<bits/stdc++.h>
#define db double
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=5e5+5;
int n,a[N];
int vis[N<<1],head[N<<1],nxt[N<<1],c[N<<1],tot=0;
inline void add(int x,int y,int z){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;}
const int rt=1;
int root[N],que[N];
struct Splay{
int fa[N],c[N][2];
ll key[N],sum[N];
int size[N],sz;
inline void pushup(int d){
sum[d]=sum[c[d][0]]+sum[c[d][1]]+key[d];
size[d]=size[c[d][0]]+size[c[d][1]]+1;
}
inline void rotate(int p,int x){
int mark=p==c[x][1],y=c[p][mark^1];
int z=fa[x];
if(x==c[z][0])c[z][0]=p;
if(x==c[z][1])c[z][1]=p;
if(y!=0)fa[y]=x;
fa[p]=z;c[p][mark^1]=x;
fa[x]=p;c[x][mark]=y;
pushup(x);
}
inline void splay(int p,int &rt){
while(fa[p]){
int x=fa[p],y=fa[x];
if(y==0)rotate(p,x);
else if(x==c[y][0]^p==c[x][0]) rotate(p,x),rotate(p,y);
else rotate(x,y),rotate(p,x);
}
rt=p;
pushup(p);
}
int newnode(){++sz;c[sz][0]=c[sz][1]=0;fa[sz]=0;return sz;}
inline void insert(ll k,int y,int pos){
int now=root[y],f=now,mark=0;
while(now){
f=now;
if(key[now]<=k)now=c[now][mark=1];
else now=c[now][mark=0];
}
now=pos;
fa[now]=f;if(f!=0)c[f][mark]=now;
c[now][0]=c[now][1]=0;key[now]=k;
splay(now,root[y]);
}
inline ll upperbound(ll k,int y){
int now=root[y],f=now;
while(now){
if(key[now]<=k){
if(f==root[y] && key[f]<=key[now]) f=now;
now=c[now][1];
}
else f=now,now=c[now][0];
}
splay(f,root[y]);
return k*size[c[f][0]]-sum[c[f][0]];
}
inline void merge(int x,int y){
if(size[root[x]]>size[root[y]]) swap(root[x],root[y]);
int head=0,tail=1;
que[head]=root[x];
while(head!=tail){
int x=que[head++];
if(c[x][0])que[tail++]=c[x][0];
if(c[x][1])que[tail++]=c[x][1];
insert(key[x],y,x);
}
}
}tt;
int fa[N];
ll dis[N],mx[N],t[N];
void dfs(int u){
for(int i=head[u];i;i=nxt[i]){
int v=vis[i];
if(v==fa[u]) continue;
fa[v]=u;dis[v]=dis[u]+c[i];
dfs(v);
mx[u]=max(mx[v]+c[i],mx[u]);
}
}
void dfs2(int u){
tt.insert(1ll<<60,u,tt.newnode());
for(int i=head[u];i;i=nxt[i]){
int v=vis[i];
if(v==fa[u])continue;
dfs2(v);tt.merge(v,u);
}
ll l=0,r=mx[u]+a[u];
while(l<r){
ll mid=l+r>>1;
if(tt.upperbound(mid+dis[u],u)>=a[u]) r=mid;
else l=mid+1;
}
t[u]=r;
tt.insert(r+dis[u],u,tt.newnode());
}
inline void file(){freopen("conquer.in","r",stdin);freopen("conquer.out","w",stdout);}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int x,y,z,i=1;i<n;i++){
x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
dfs(rt);dfs2(rt);
ll ans=0;
for(int i=1;i<=n;i++) ans=max(ans,t[i]);
cout<<ans;
return 0;
}