704 Codeforces Round #366 (Div. 1)

A. Thor

题意:n个程序,Q次操作,操作有三个类型,

1:程序x生成一个未读通知

2:把程序x生成的所有通知阅读

3:阅读前t个通知(不仅是未读通知)

每次操作完输出未读通知个数

n , Q ≤ 3 e 5 n,Q\leq3e5 n,Q3e5

这个题炸一看并不是很好做,但只要不想复杂了其实直接用两个链表+并查集维护没被删掉的即可

注意这里链表最好就不要偷懒了,容易出奇怪的错最后发现还是要老老实实俩个双向

十年没写并查集路径压缩都不会了。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int _=1e2;
const int maxn=3e5+_;
 
int ans;bool v[maxn]; 
int L[maxn],R[maxn],cl[maxn],cr[maxn],clast[maxn];
int findL(int x){return !v[x]?x:L[x]=findL(L[x]);}
int findR(int x){return !v[x]?x:R[x]=findR(R[x]);}
int findcl(int x){return !v[x]?x:cl[x]=findcl(cl[x]);}
int findcr(int x){return !v[x]?x:cr[x]=findcr(cr[x]);}
 
void del(int x)
{
	v[x]=true;ans--;
	int l=findL(x),r=findR(x);
	R[l]=r;R[r]=l;
	l=findcl(x),r=findcr(x);
	cr[l]=r,cl[r]=l;
}
int main()
{
	int n,Q,op,x,last; ans=0;
	scanf("%d%d",&n,&Q);last=0;
	while(Q--)
	{
		scanf("%d%d",&op,&x);
		if(op==1)
		{
			last++;ans++;
			L[last]=last-1;
			cl[last]=clast[x];
			clast[x]=last;
		}
		else if(op==2)
		{
			int k=findcl(clast[x]);
			while(k!=0)del(k),k=findcl(k);
			clast[x]=0;
		}
		else if(op==3)
		{
			int k=findL(x);
			while(k!=0)del(k),k=findL(k);
		}
		printf("%d\n",ans);
	}
	
	return 0;
}
B. Ant Man

题意:有n把椅子,从起始椅子出发经过所有椅子恰好一次到达结束椅子,椅子之间随便跳,对于答案的贡献和向左还是向右有关,求最小值。 n ≤ 5000 n\leq5000 n5000

关于答案的变量比较多注意区分

考虑每个点的贡献,除起止以外每个点会有一出一入,共四种情况

注意到我们并不关心具体从那个点跳过来,或者跳去那个点,只需要知道从那个方向跳过来即可

数据范围提示 O ( n 2 ) O(n^2) O(n2),来个二维的DP

方向性和顺序有关,设一维为当前考虑第i个位置的点从那个方向跳过来

此时在它之前考虑的一定在它左边,那么就可以用上分段的套路了

再设一维表示当前连了有多少段(最终目标是连出一条路径),假如我当前点想要从左边跳过来,那么就接在一段的后面上,否则不接,假如想跳到左边,就接在一段的前面。结合注释不难理解。

注意特判st、ed

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=5e3+_;
 
LL x[maxn],a[maxn],b[maxn],c[maxn],d[maxn];
LL f[maxn][maxn];//当前玩到第几个,有多少段了 的费用 
int main()
{
	int n,st,ed;
	scanf("%d%d%d",&n,&st,&ed);
	for(int i=1;i<=n;i++)scanf("%lld",&x[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&c[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&d[i]);
	
	int o=0;
	memset(f,31,sizeof(f));f[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			if(i!=st&&i!=ed)
			{
				//右来右去 新建一段  
				f[i][j]=min(f[i][j],f[i-1][j-1]+b[i]+d[i]-2*x[i]);
				
				//右来左去 接在前面
				if(!(o&1)||j>1)
					f[i][j]=min(f[i][j],f[i-1][j]+b[i]+c[i]);
				
				//左来右去 接在后面
				if(!(o&2)||j>1)
					f[i][j]=min(f[i][j],f[i-1][j]+a[i]+d[i]);
				
				//左来左去 连接两段
				if((o&3)!=3||i==n||j>1)
					f[i][j]=min(f[i][j],f[i-1][j+1]+a[i]+c[i]+2*x[i]);
			}
			else if(i==st)
			{
				//向右 新建一段 
				f[i][j]=min(f[i][j],f[i-1][j-1]+d[i]-x[i]);
				//向左 接在前面 
				if((!o&2)||j>1||i==n)
					f[i][j]=min(f[i][j],f[i-1][j]+c[i]+x[i]);
			}
			else
			{
				//右来 新建一段
				f[i][j]=min(f[i][j],f[i-1][j-1]+b[i]-x[i]);
				//左来 接在后面
				if((!o&1)||j>1||i==n)
					f[i][j]=min(f[i][j],f[i-1][j]+a[i]+x[i]);
			}
			
			if(f[i][j]>f[0][1])f[i][j]=f[0][1];
		}
		if(i==st)o^=1;
		if(i==ed)o^=2;
	}
	printf("%lld\n",f[n][1]);
	
	return 0;
}
C. Black Widow

题意:求满足下列柿子的方案数(每个v都是给出的变量) k ≤ 2 k\leq2 k2,每个变量出现次数 ≤ 2 \leq2 2
在这里插入图片描述
把每个组看成一个点,即求有奇数个点黑了的方案数

主要在如何利用变量最多2个和组中变量最多2个

对于变量出现0次的,最后答案*2即可,1次的只会对当前组影响,取0取1对于其他要算的正好互补(取0要算出现奇数次,取1要算出现偶数次)对于出现2次就很显然了,直接建边

又因为度数最大为2,则每个联通块一定是链或者简单环,分情况DP即可

挺难写的啊。

D. Captain America

题意:平面直角坐标系中有n个点,每个点必须涂成红或蓝色,要付出不同的代价,有m条约束,表示某一行或列红色和蓝色的绝对差不超过给定的数,求最小费用并输出方案 n , m ≤ 1 e 5 , x i , y i ≤ 1 e 9 n,m\leq1e5,xi,yi\leq1e9 n,m1e5,xi,yi1e9

首先先转化模型,这个是个经典的把行列看成点,点看成边的题,对于绝对差的限制可以变成上下界。

跑一个最大/小流,然后让比较贵的颜色少画就好了

比较坑爹的是有些边没有点然后限制又设上去完全没用,但是离散化用lower_bound的时候要出点问题。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=1e5+_;
const int maxm=1e5+_;
const int maxp=4*maxn;
const LL inf=(1LL<<62);
 
struct node
{
	int x,y,next; bool b; LL c;
}a[2*maxp];int len,last[maxp];
void ins(int x,int y,LL c)
{
//	printf("%d %d %lld\n",x,y,c);
	len++;
	a[len].x=x;a[len].y=y;a[len].c=c;a[len].b=true;
	a[len].next=last[x];last[x]=len;
	len++;
	a[len].x=y;a[len].y=x;a[len].c=0;a[len].b=true;
	a[len].next=last[y];last[y]=len;
}
int st,ed;
int head,tail,list[maxp],h[maxp];
bool bt_h()
{
	head=1,tail=2;list[1]=st;
	memset(h,0,sizeof(h));h[st]=1;
	while(head!=tail)
	{
		int x=list[head];
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(h[y]==0&&a[k].c>0&&a[k].b)
			{
				h[y]=h[x]+1;
				list[tail++]=y;
			}
		}
		head++;
	}
	return h[ed]!=0;
}
int cur[maxp];
LL findflow(int x,LL f)
{
	if(x==ed)return f;
	LL s=0;
	for(int k=cur[x];k;k=a[k].next)
	{
		int y=a[k].y;cur[x]=k;
		if(a[k].c>0&&h[y]==h[x]+1&&a[k].b)
		{
			LL t=findflow(y,min(f-s,a[k].c));
			s+=t,a[k].c-=t;a[k^1].c+=t;
			if(s==f)break;
		}
	}
	if(s==0)h[x]=0;
	return s;
}
 
LL L[maxp],R[maxp],c[maxp];
int px[maxn],py[maxn];
int n,m,S,lsxlen;LL red,blue,du[maxp];
void solve()
{
	st=n+1,ed=st+1; len=1;
	for(int i=1;i<=n;i++)
	{
		if(i<=lsxlen)ins(st,i,R[i]-L[i]),du[st]-=L[i],du[i]+=L[i];
		else ins(i,ed,R[i]-L[i]),du[i]-=L[i],du[ed]+=L[i];
	}
	ins(ed,st,inf);
	int id=len;
	
	st=ed+1,ed=st+1;
	for(int i=1;i<=n+2;i++)
		if(du[i]<0)ins(i,ed,-du[i]);
		else if(du[i]>0)ins(st,i,du[i]);
		
	int td=len+1;
	for(int i=1;i<=m;i++)
		ins(px[i],py[i],1);
		
	LL ans=0;
	while(bt_h())
	{
		for(int i=1;i<=2*n+4;i++)cur[i]=last[i];
		ans+=findflow(st,inf);
	}
	for(int i=last[st];i;i=a[i].next)
	if(a[i].c!=0){puts("-1");return ;}
	
	ed=st-1,st=ed-1; a[id].b=false,a[id^1].b=false;
	while(bt_h())
	{
		for(int i=1;i<=2*n+4;i++)cur[i]=last[i];
		ans+=findflow(st,inf);
	}
	
	char ca='r',cb='b';
	if(red>blue)swap(red,blue),swap(ca,cb);
	LL sum=0;
	for(int i=td;i<=len;i+=2)
		sum+=(a[i].c==0)?red:blue;
	printf("%lld\n",sum);
	for(int i=td;i<=len;i+=2)
		printf("%c",(a[i].c==0)?ca:cb);
	puts("");
}
 
int lsx[maxn],lsylen,lsy[maxn];
int main()
{
	scanf("%d%d%d%d",&n,&S,&red,&blue);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&px[i],&py[i]),lsx[i]=px[i],lsy[i]=py[i];
	sort(lsx+1,lsx+n+1);
	sort(lsy+1,lsy+n+1);
	lsxlen=unique(lsx+1,lsx+n+1)-lsx-1;
	lsylen=unique(lsy+1,lsy+n+1)-lsy-1;
	for(int i=1;i<=n;i++)
		px[i]=lower_bound(lsx+1,lsx+lsxlen+1,px[i])-lsx,c[px[i]]++,
		py[i]=lower_bound(lsy+1,lsy+lsylen+1,py[i])-lsy+lsxlen,c[py[i]]++;
	
	memset(L,0,sizeof(L));
	memset(R,63,sizeof(R));
	int op,x,tt,d;
	for(int i=1;i<=S;i++)
	{
		scanf("%d%d%d",&op,&tt,&d);
		if(op==1)
		{
			x=lower_bound(lsx+1,lsx+lsxlen+1,tt)-lsx;
			if(lsx[x]!=tt)continue;
		}
		else
		{
			x=lower_bound(lsy+1,lsy+lsylen+1,tt)-lsy;
			if(lsy[x]!=tt)continue;x+=lsxlen;
		}
		R[x]=min(R[x],min(c[x],(c[x]+d)/2)),L[x]=c[x]-R[x];
		if(R[x]<L[x]){puts("-1");return 0;}
	}
	m=n;
	n=lsxlen+lsylen;
	solve();
	
	return 0;
}
E. Iron Man

题意:给一棵树,有m个钢铁侠,每个钢铁侠从ti时刻从vi出现并前往ui,速度为ci条道路/秒,问第一次碰撞的时间

考虑在一条链的情况,相当于在以时间为x轴,位置为y轴的坐标系中,有多条线段,求离y轴最近的交点

以时间扫扫描线插入删除,按当前位置顺序维护一棵平衡树,不难想象当有交点时有两条线段位置交互

对于当前加入的线段,如果它要和别人相交,一定会先和前驱或后继交,删除时有新的两点相邻,也需判断

我用的两点式表示一条线段,相交用multi判,求交点转斜截式

回到树上,我们可以树链剖分,然后把每一条移动的路径拆分到链,然后按上述做法解决。(注意轻边也要被分配和计算答案)

被卡常了 TLE的code

或许应该用multiset,判相交我的方法麻烦了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=1e5+_;
const double eps=1e-9;
inline bool eq(double x,double y){return fabs(x-y)<=eps;}
struct edge
{
	double x,y,st,ed;
	edge(){}
	edge(double X,double Y,double ST,double ED){x=X,y=Y,st=ST,ed=ED;}
};vector<edge>vec[maxn];
struct point{double x,y;point(){}point(double X,double Y){x=X,y=Y;}};
 
namespace P
{
	int n,m;
	struct node
	{
		int x,y,next;
	}a[2*maxn];int len,last[maxn];
	void ins(int x,int y)
	{
		len++;
		a[len].x=x;a[len].y=y;
		a[len].next=last[x];last[x]=len;
	}
	int f[30][maxn],son[maxn],tot[maxn],dep[maxn];
	void pre_tree_node(int x)
	{
		for(int i=1;(1<<i)<=dep[x];i++)f[i][x]=f[i-1][f[i-1][x]];
		tot[x]=1;
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(y!=f[0][x])
			{
				f[0][y]=x;
				dep[y]=dep[x]+1;
				pre_tree_node(y);
				if(tot[son[x]]<tot[y])son[x]=y;
				tot[x]+=tot[y];
			}
		}
	}
	int z,ys[maxn],bel[maxn],fid[maxn],hed[maxn],tal[maxn];
	void pre_tree_edge(int x,int id,int rk)
	{
		bel[x]=id,ys[x]=rk;
		if(son[x]==0){tal[id]=x;return ;}
		pre_tree_edge(son[x],id,rk+1);
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(y!=f[0][x]&&y!=son[x])
			{
				hed[++z]=x,tal[z]=y;fid[y]=z;
				hed[++z]=y,pre_tree_edge(y,z,1);
			}
		}
	}
	int LCA(int x,int y)
	{
		if(dep[x]<dep[y])swap(x,y);
		for(int i=25;i>=0;i--)
			if(dep[x]-dep[y]>=(1<<i))x=f[i][x];
		if(x==y)return x;
		for(int i=25;i>=0;i--)
			if(dep[x]>=(1<<i)&&f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
		return f[0][x];
	}
	void main()
	{
		int x,y;
		scanf("%d%d",&n,&m); 
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			ins(x,y),ins(y,x);
		}
		pre_tree_node(1);
		z=1;hed[z]=1;pre_tree_edge(1,1,1);
		double st,ed,v;
		for(int i=1;i<=m;i++)
		{
			scanf("%lf%lf%d%d",&st,&v,&x,&y);
			int z=LCA(x,y);
			while(bel[x]!=bel[z])
			{
				vec[bel[x]].push_back(edge(ys[x],ys[hed[bel[x]]],st,st+(dep[x]-dep[hed[bel[x]]])/v));
				st+=(dep[x]-dep[hed[bel[x]]])/v;
				vec[fid[x]].push_back(edge(1,2,st,st+1.0/v));
				st+=1.0/v;
				x=f[0][hed[bel[x]]];
			}
			if(x!=z)
			{
				vec[bel[x]].push_back(edge(ys[x],ys[z],st,st+(dep[x]-dep[z])/v));
				st+=(dep[x]-dep[z])/v;
			}
			
			ed=st+(dep[y]-dep[z])/v;
			while(bel[z]!=bel[y])
			{
				vec[bel[y]].push_back(edge(ys[hed[bel[y]]],ys[y],ed-(dep[y]-dep[hed[bel[y]]])/v,ed));
				ed-=(dep[y]-dep[hed[bel[y]]])/v;
				vec[fid[y]].push_back(edge(2,1,ed-1.0/v,ed));
				ed-=1.0/v;
				y=f[0][hed[bel[y]]];
			}
			if(y!=z)
			{
				vec[bel[y]].push_back(edge(ys[z],ys[y],ed-(dep[y]-dep[z])/v,ed));
				ed-=(dep[y]-dep[z])/v;
			}
		}
	}
}
 
namespace J
{
	double multi(point p1,point p2,point p0)
	{
		double x1,y1,x2,y2;
		x1=p1.x-p0.x;
		y1=p1.y-p0.y;
		x2=p2.x-p0.x;
		y2=p2.y-p0.y;
		return x1*y2-x2*y1;
	}
	bool jd(point p1,point p2,point p3,point p4)
	{
		if( eq(multi(p3,p2,p1),0)&&( eq(p3.x,p1.x)||eq(p3.x,p2.x)||((p3.x<p1.x)^(p3.x<p2.x)) ) ||
			eq(multi(p4,p2,p1),0)&&( eq(p4.x,p1.x)||eq(p4.x,p2.x)||((p4.x<p1.x)^(p4.x<p2.x)) ) ||
			eq(multi(p1,p4,p3),0)&&( eq(p1.x,p3.x)||eq(p1.x,p4.x)||((p1.x<p3.x)^(p1.x<p4.x)) ) ||
			eq(multi(p2,p4,p3),0)&&( eq(p2.x,p3.x)||eq(p2.x,p4.x)||((p2.x<p3.x)^(p2.x<p4.x)) ) )return true;
		if((multi(p3,p2,p1)<0)^(multi(p4,p2,p1)<0))return true;
		return false;
	}
	double getjdy(point p1,point p2,point p3,point p4)
	{
		double k1=(p1.y-p2.y)/(p1.x-p2.x),k2=(p3.y-p4.y)/(p3.x-p4.x);
		double b1=p1.y-k1*p1.x,b2=p3.y-k2*p3.x;
		if(eq(k1,k2))
		return max(p1.x,p3.x);
		else return (b2-b1)/(k1-k2);
	}
}
 
namespace S
{
	#define q vec[o]
	struct node
	{
		point A,B;
		int f,son[2];
		double gety(double x){return (x-B.x)/(A.x-B.x)*(A.x-B.x)+A.y;}
	}tr[maxn];int trlen,root;double nowtim;
	void rotate(int x,int w)
	{
		int f=tr[x].f,ff=tr[f].f;
		int R,r;
		
		R=f,r=tr[x].son[w];
		tr[R].son[1-w]=r;
		if(r!=0)tr[r].f=R;
		
		R=ff,r=x;
		if(tr[ff].son[0]==f)tr[R].son[0]=r;
		else tr[R].son[1]=r;
		tr[r].f=R;
		
		R=x,r=f;
		tr[R].son[w]=r;
		tr[r].f=R;
	}
	void splay(int x,int rt)
	{
		while(tr[x].f!=rt)
		{
			int f=tr[x].f,ff=tr[f].f;
			if(ff==rt)
			{
				if(tr[f].son[0]==x)rotate(x,1);
				else rotate(x,0);
			}
			else
			{
					 if(tr[ff].son[0]==f&&tr[f].son[0]==x)rotate(f,1),rotate(x,1);
				else if(tr[ff].son[1]==f&&tr[f].son[1]==x)rotate(f,0),rotate(x,0);
				else if(tr[ff].son[0]==f&&tr[f].son[1]==x)rotate(x,1),rotate(x,0);
				else if(tr[ff].son[1]==f&&tr[f].son[0]==x)rotate(x,0),rotate(x,1);
			}
		}
		if(rt==0)root=x;
	}
	int ffind(int now,int w)
	{
		splay(now,0);
		if(tr[now].son[w]==0)return -1;
		now=tr[now].son[w];
		while(tr[now].son[w^1]!=0)now=tr[now].son[w^1];
		return now;
	}
	double ans;
	bool ins(point A,point B)
	{
		int now=++trlen;
		tr[now].A=A,tr[now].B=B;tr[now].f=0;
		tr[now].son[0]=tr[now].son[1]=0;
		if(trlen==1){root=1;return false;}
		int pos=root;
		while(1)
		{
			if(tr[pos].gety(nowtim)>tr[now].A.y)
			{
				if(tr[pos].son[0]!=0)pos=tr[pos].son[0];
				else {tr[pos].son[0]=now,tr[now].f=pos;break;}
			}
			else 
			{
				if(tr[pos].son[1]!=0)pos=tr[pos].son[1];
				else {tr[pos].son[1]=now,tr[now].f=pos;break;}
			}
		}
		for(int i=0;i<=1;i++)
		{
			int pos=ffind(now,i);
			if(pos!=-1)
			{
				if(J::jd(tr[pos].A,tr[pos].B,tr[now].A,tr[now].B))
					ans=min(ans,J::getjdy(tr[pos].A,tr[pos].B,tr[now].A,tr[now].B));
			}
		}
		return !eq(ans,(1<<30));
	}
	bool del(int x)
	{
		int L=ffind(x,0),R=ffind(x,1);
		if(L==-1&&R==-1){root=trlen=0;return false;}
		else if(L==-1){splay(R,x),tr[R].f=0,root=R;return false;}
		else if(R==-1){splay(L,x),tr[L].f=0,root=L;return false;}
		splay(L,0),splay(R,L);
		tr[R].son[0]=0;
		if(J::jd(tr[L].A,tr[L].B,tr[R].A,tr[R].B))
			ans=J::getjdy(tr[L].A,tr[L].B,tr[R].A,tr[R].B);
		return !eq(ans,(1<<30));
	}
	
	struct ttt
	{
		int op,pos;double t;
		ttt(){}ttt(int OP,int POS,double T){op=OP,pos=POS,t=T;}
	}li[maxn];int lilen,id[maxn];
	bool cmp(ttt t1,ttt t2){return eq(t1.t,t2.t)?t1.op<t2.op:t1.t<t2.t;}
	double main(int o)
	{
		lilen=0;
		for(int i=0;i<q.size();i++)
		{
			li[++lilen]=ttt(0,i,q[i].st);
			li[++lilen]=ttt(1,i,q[i].ed);
		}
		sort(li+1,li+lilen+1,cmp);
		nowtim=0;trlen=root=0;
		int u;ans=(1<<30);
		for(int i=1;i<=lilen;i++)
		{
			u=li[i].pos;
			if(li[i].op==0)
			{
				if(ins(point(q[u].st,q[u].x),point(q[u].ed,q[u].y)))break;
				id[u]=trlen;
			}
			else if(del(id[u]))break;
			nowtim=li[i].t;
		}
		return ans;
	}
	#undef q
}
 
int main()
{
	P::main();
	double ans=(1<<30);
	for(int i=1;i<=P::z;i++)
		ans=min(ans,S::main(i));
	if(ans==(1<<30))puts("-1");
	else printf("%.8lf\n",ans);
		
 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值