SDOI一轮过去,觉得自己爆搜都会写残。。。好无奈。。。
先修课过去,觉得自己手速慢加各种爆搜残。。。好无奈。。。
day1T2
题目大意:一棵树,给出边的长度,每次操作改变一个点的信息(标记或取消标记),之后输出从一个点出发、走过所有标记点之后回到起始点的最小距离。
思路:一轮的时候只能弱弱的写暴力,下来听TA的题解,看TA各种ac。。。其实我们每次加入一个点,都要使他尽快并到已有的路线上去,这时候我们要找两个lca:所有原来点的lca1(也就是dfs序里最两边的标记点的lca),与这个点两边两个点(也就是dfs序中离这个点最近的两个标记点)的两个lca中离这个点较近的lca2(一开始理解错了这个lca,这里距离最近才能保证答案最小)。这两个lca一定是祖孙关系(可能是一个点)。然后就分类讨论一下:如果lca1是lca2的祖先,答案就是新加入点道lca2的距离;如果反过来,答案还要加上lca1到lca2的距离。这里的距离以及求lca所要用的信息都可以在建树的时候求出(第一次写了非递归的建树)。用一个线段树来维护一下dfs序中点被标记的情况。在插入和删除的时候,要注意修改和查询线段树的顺序。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define inf 2100000000LL #define proof 17 using namespace std; struct use{ int st,en,va; }edge[200001]={0}; struct used{ int hi; long long dis; }dian[100001]={0}; struct uss{ int maxp,minp,num; }tree[400000]; int tot=0,next[200001]={0},point[100001]={0},bi[100001]={0},zhan[100001]={0},kk[100001]={0},cur[100001]={0},ff[100001][18]={0}; bool visit[100001]={false}; void add(int st,int en,int va) { ++tot;next[tot]=point[st];point[st]=tot; edge[tot].st=st;edge[tot].en=en;edge[tot].va=va; ++tot;next[tot]=point[en];point[en]=tot; edge[tot].st=en;edge[tot].en=st;edge[tot].va=va; } void updata(int i) { tree[i].maxp=max(tree[i*2].maxp,tree[i*2+1].maxp); tree[i].minp=min(tree[i*2].minp,tree[i*2+1].minp); tree[i].num=tree[i*2].num+tree[i*2+1].num; } void build(int i,int l,int r) { int mid; if (l==r) { tree[i].maxp=-inf;tree[i].minp=inf; tree[i].num=0;return; } mid=(l+r)/2; build(i*2,l,mid);build(i*2+1,mid+1,r); updata(i); } void ins(int i,int l,int r,int x,int y) { int mid; if (l==r) { tree[i].num+=y; if (tree[i].num) tree[i].maxp=tree[i].minp=l; else { tree[i].maxp=-inf;tree[i].minp=inf; } return; } mid=(l+r)/2; if (x<=mid) ins(i*2,l,mid,x,y); else ins(i*2+1,mid+1,r,x,y); updata(i); } int ask(int i,int l,int r,int ll,int rr,int kk) { int mid,aaa; if (ll<=l&&r<=rr) { if (kk) return tree[i].maxp; else return tree[i].minp; } mid=(l+r)/2; if (kk) aaa=-inf; else aaa=inf; if (ll<=mid) { if (kk) aaa=max(aaa,ask(i*2,l,mid,ll,rr,kk)); else aaa=min(aaa,ask(i*2,l,mid,ll,rr,kk)); } if (rr>mid) { if (kk) aaa=max(aaa,ask(i*2+1,mid+1,r,ll,rr,kk)); else aaa=min(aaa,ask(i*2+1,mid+1,r,ll,rr,kk)); } return aaa; } int lca(int x,int y) { int i,j; if (x==y) return x; if (dian[x].hi>dian[y].hi) swap(x,y); for (i=proof;i>=0;--i) if (dian[x].hi<=dian[ff[y][i]].hi) y=ff[y][i]; if (x==y) return x; for (i=proof;i>=0;--i) { if (ff[x][i]!=ff[y][i]) { x=ff[x][i];y=ff[y][i]; } } return ff[x][0]; } int main() { int n,m,i,j,u,v,w,x,p1,p2,lca1,lca2,lca3; long long ans=0; scanf("%d%d",&n,&m); for (i=1;i<n;++i) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); } for (i=1;i<=n;++i) cur[i]=point[i]; i=1;zhan[i]=1;u=1;bi[1]=1;visit[1]=true; while(i) { x=zhan[i]; for (j=1;j<=proof;++j) ff[bi[x]][j]=ff[ff[bi[x]][j-1]][j-1]; while (cur[x]) { j=cur[x];cur[x]=next[cur[x]]; if (!visit[edge[j].en]) { ++i;++u;zhan[i]=edge[j].en; bi[edge[j].en]=u;dian[u].dis=dian[bi[x]].dis+(long long)edge[j].va; dian[u].hi=i-1;ff[u][0]=bi[x]; visit[edge[j].en]=true;break; } } if (!cur[zhan[i]]) --i; } build(1,1,n); for (i=1;i<=m;++i) { scanf("%d",&j); j=bi[j];kk[j]=1-kk[j];u=j; if (tree[1].num==0) { printf("0\n");ins(1,1,n,j,1);continue; } if (tree[1].num<=2&&kk[j]==0) { printf("0\n");ins(1,1,n,j,-1);continue; } if (j>1) p1=ask(1,1,n,1,j-1,1); else p1=-inf; if (j<n) p2=ask(1,1,n,j+1,n,0); else p2=inf; if (kk[j]) { lca1=lca(tree[1].minp,tree[1].maxp); ins(1,1,n,j,1); lca2=lca3=0; if (p1>-inf) lca2=lca(p1,j); if (p2<inf) lca3=lca(p2,j); if ((lca2==0)||(lca2!=0&&dian[lca2].dis<dian[lca3].dis)) lca2=lca3; if (dian[lca2].dis>=dian[lca1].dis) ans+=dian[j].dis-dian[lca2].dis; if (dian[lca2].dis<dian[lca1].dis) ans+=dian[j].dis-dian[lca2].dis+dian[lca1].dis-dian[lca2].dis; } else { ins(1,1,n,j,-1); lca1=lca(tree[1].minp,tree[1].maxp); lca2=lca3=0; if (p1>-inf) lca2=lca(p1,j); if (p2<inf) lca3=lca(p2,j); if ((lca2==0)||(lca2!=0&&dian[lca2].dis<dian[lca3].dis)) lca2=lca3; if (dian[lca2].dis>=dian[lca1].dis) ans-=(dian[j].dis-dian[lca2].dis); if (dian[lca2].dis<dian[lca1].dis) ans-=(dian[j].dis-dian[lca2].dis+dian[lca1].dis-dian[lca2].dis); } printf("%lld\n",ans*2); } }
day2T1
题目大意:有n个敌人,m个武器,每种武器有特定的攻击敌人,伤害不同,求最短消灭所有敌人的时间。
思路:因为有小数,我们乘一个1000000,到整数上,然后二分答案,跑网络流(每种武器在二分时间的最多的伤害已知,如果到汇点满流,就是可行)。
注意longlong和int之间一些强转。。。
#include<iostream> #include<cstdio> #include<cstring> #define inf 1000000 #define linf 9223372036854775806LL using namespace std; struct use{ int st,en; long long va; }edge[10000]={0}; int a[100]={0},b[100]={0},tot,map[100][100],bb[100]={0},ba[100]={0},n,m,point[200]={0},next[10000]={0}, gap[200]={0},cur[200]={0},dis[200]={0},pre[200]={0}; void add(int st,int en,long long va) { ++tot;next[tot]=point[st];point[st]=tot; edge[tot].st=st;edge[tot].en=en;edge[tot].va=va; ++tot;next[tot]=point[en];point[en]=tot; edge[tot].st=en;edge[tot].en=st;edge[tot].va=0; } long long sap(int st,int en) { int i,j,u; long long minn,ans=0; bool f=false; memset(gap,0,sizeof(gap)); memset(dis,0,sizeof(dis)); memset(pre,0,sizeof(pre)); gap[0]=en-st+1;u=st; for (i=st;i<=en;++i) cur[i]=point[i]; while(dis[st]<en-st+1) { f=false; for (i=cur[u];i;i=next[i]) { if (edge[i].va&&dis[edge[i].en]+1==dis[u]) { f=true;cur[u]=i;break; } } if (f) { pre[edge[i].en]=i;u=edge[i].en; if (u==en) { minn=linf; for (i=en;i!=st;i=edge[pre[i]].st) minn=min(minn,edge[pre[i]].va); ans+=minn; for (i=en;i!=st;i=edge[pre[i]].st) { edge[pre[i]].va-=minn; edge[pre[i]^1].va+=minn; } u=st; } } else { --gap[dis[u]]; if (!gap[dis[u]]) return ans; minn=en-st+1; for (i=point[u];i;i=next[i]) if (edge[i].va&&dis[edge[i].en]<minn) minn=dis[edge[i].en]; cur[u]=point[u]; dis[u]=minn+1;++gap[dis[u]]; if (u!=st) u=edge[pre[u]].st; } } return ans; } bool judge(long long tim,int en) { int i,j; long long sum=0,k; tot=1; memset(point,0,sizeof(point)); memset(next,0,sizeof(next)); for (i=1;i<=m;++i) add(1,bb[i],tim*(long long)b[i]); for (i=1;i<=m;++i) for (j=1;j<=n;++j) if (map[i][j]) add(bb[i],ba[j],linf); for (j=1;j<=n;++j) { add(ba[j],en,(long long)inf*(long long)a[j]); sum+=(long long)inf*(long long)a[j]; } k=sap(1,en); if (k==sum) return true; else return false; } int main() { int i,j,sum=0,minn,en; long long ll,rr,mid; double ans; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) { scanf("%d",&a[i]); sum+=a[i]; ba[i]=m+i+1; } minn=2100000000; for (j=1;j<=m;++j) { scanf("%d",&b[j]); minn=min(minn,b[j]); bb[j]=j+1; } en=2+m+n; for (i=1;i<=m;++i) for (j=1;j<=n;++j) scanf("%d",&map[i][j]); ll=0;rr=(long long)(sum/minn)*(long long)inf; while(ll<rr) { mid=(ll+rr)/2; if (judge(mid,en)) rr=mid; else ll=mid+1; } ans=ll*1.0/inf; printf("%.6f\n",ans); }
时间: 2024-11-05 16:09:26