[CQOI2017]Round 1

day1

小Q的棋盘

Description
小 Q 正在设计一种棋类游戏。
在小 Q 设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的格点之间移动。整个棋盘上共有 V 个格点,编号为0,1,2 … , N − 1,它们是连通的,也就是说棋子从任意格点出发,总能到达所有的格点。小 Q 在设计棋盘时,还保证棋子从一个格点移动到另外任一格点的路径是唯一的。
小 Q 现在想知道,当棋子从格点 0 出发,移动 N 步最多能经过多少格点。格点可以重复经过多次,但不重复计数。

HINT
\(N,V\leq100, 0\leq a_i,b_i\leq N\).

Solution
随便树形 DP 一下就好了 ( \(f[i][j][0/1]\) 表示在以 i 为根的子树中走 j 步是否回到 i 所需最少步数 ) .

#define N 105
struct graph{
	int nxt,to;
}e[N<<1];
int f1[N][N],f2[N][N],g[N],n,m,cnt,ans;
bool v[N];
inline void addedge(int x,int y){
	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline void dfs(int u){
	for(int i=g[u],k;i;i=e[i].nxt)
		if(!v[k=e[i].to]){
			v[e[i].to]=true;dfs(e[i].to);
			for(int j=m;j>=0;--j)
				for(int l=0;l+2<=j;++l)
					f1[u][j]=max(f1[u][j],f2[k][l]+f1[u][j-l-2]);
			for(int j=m;j>=0;--j)
				for(int l=0;l+1<=j;++l)
					f1[u][j]=max(f1[u][j],f1[k][l]+f2[u][j-l-1]);
			for(int j=m;j>=0;--j)
				for(int l=0;l+2<=j;++l)
					f2[u][j]=max(f2[u][j],f2[k][l]+f2[u][j-l-2]);
		}
	for(int i=0;i<=m;++i) ++f1[u][i],++f2[u][i];
}
inline void Aireen(){
	scanf("%d%d",&n,&m);
	for(int i=1,j,k;i<n;++i){
		scanf("%d%d",&j,&k);++j;++k;
		addedge(j,k);addedge(k,j); 
	}
	v[1]=true;dfs(1);
	for(int i=0;i<=m;++i)
		ans=max(ans,max(f1[1][i],f2[1][i]));
	printf("%d\n",ans);
}

小Q的草稿

Description
小 Q 是个程序员。
众所周知,程序员在写程序的时候经常需要草稿纸。小 Q 现在需要一张草稿纸用来画
图,但是桌上只有一张草稿纸,而且是一张被用过很多次的草稿纸。
草稿纸可以看作一个二维平面,小 Q 甚至已经给它建立了直角坐标系。以前每一次草稿使用过的区域,都可以近似的看作一个平面上的一个三角形,这个三角形区域的内部和边界都不能再使用。当然了,以前的草稿也没有出现区域重叠的情况。
小 Q 已经在草稿纸上画上了一些关键点,这些关键点都在没使用过的区域。小 Q 想把这些关键点两两之间尽可能的用线段连接起来。连接两个关键点的线段有可能会穿过已经用过的草稿区域, 这样显然不允许。于是小 Q 就想知道,有多少对关键点可以被线段连接起来,而且还不会穿过已经用过的区域。为了方便,小 Q 保证任意三个关键点不会共线。

Solution
我只会 \(O(N^2)\) 的算法.

判断两点之间连线与三角形三边会不会有交即可.

#define M 4
#define N 1005
typedef long long ll;
struct point{
	int x,y;
}a[N],b[N][M];
int n,m,ans;
ll operator * (point x,point y){
	return 1ll*x.x*y.y-1ll*x.y*y.x;
}
point operator - (point x,point y){
	return (point){x.x-y.x,x.y-y.y};
}
inline int cmp(ll x){
	return x?(x>0?1:-1):0;
}
inline bool onseg(point p,point a,point b){
    if(cmp((a-p)*(b-p))) return false;
    return cmp(a.x-p.x)*cmp(b.x-p.x)<=0&&cmp(a.y-p.y)*cmp(b.y-p.y)<=0;
}
inline bool inter(point x,point y,point u,point v){
	if(x.x>y.x) swap(x,y);
	if(u.x>v.x) swap(u,v);
	if(x.x>v.x||y.x<u.x) return false;
	if(x.y>y.y) swap(x,y);
	if(u.y>v.y) swap(u,v);
	if(x.y>v.y||y.y<u.y) return false;
	return cmp((u-x)*(y-x))*cmp((v-x)*(y-x))<=0&&cmp((x-u)*(v-u))*cmp((y-u)*(v-u))<=0;
}
inline bool chk(point x,point y,int z){
	return !inter(x,y,b[z][1],b[z][2])&&!inter(x,y,b[z][1],b[z][3])&&!inter(x,y,b[z][3],b[z][2]);
}
inline void Aireen(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%d%d",&a[i].x,&a[i].y);
	for(int i=1;i<=m;++i)
		for(int j=1;j<M;++j)
			scanf("%d%d",&b[i][j].x,&b[i][j].y);
	bool flag;
	for(int i=1;i<n;++i)
		for(int j=i+1;j<=n;++j){
			flag=true;
			for(int k=m;k;--k)
				if(!chk(a[i],a[j],k)){
					flag=false;break;
				}
			if(flag) ++ans;
		}
	printf("%d\n",ans);
}

小Q的表格

Description
小 Q 是个程序员。
作为一个年轻的程序员,小 Q 总是被老 C 欺负,老 C 经常把一些麻烦的任务交给小 Q 来处理。每当小 Q 不知道如何解决时,就只好向你求助。
为了完成任务,小 Q 需要列一个表格,表格有无穷多行,无穷多列,行和列都从 1 开始标号。为了完成任务,表格里面每个格子都填了一个整数,为了方便描述,小 Q 把第 a 行第 b 列的整数记为 f(a,b) 。为了完成任务,这个表格要满足一些条件:

  1. 对任意的正整数 a,b,都要满足 \(f(a,b)=f(b,a)\);
  2. 对任意的正整数 a,b,都要满足 \(b\times f(a,a+b)=(a+b)\times f(a,b)\)

为了完成任务,一开始表格里面的数很有规律,第 a 行第 b 列的数恰好等于 \(a\times b\) 。显然一开始是满足上述两个条件的。为了完成任务,小 Q 需要不断的修改表格里面的数,每当修改了一个格子的数之后,为了让表格继续满足上述两个条件,小 Q 还需要把这次修改能够波及到的全部格子里都改为恰当的数。由于某种神奇的力量驱使,已经确保了每一轮修改之后所有格子里的数仍然都是整数。为了完成任务,小 Q 还需要随时获取前 k 行前 k 列这个有限区域内所有数的和是多少,答案可能比较大,只需要算出答案 mod 1,000,000,007 之后的结果。

共有 m 次操作, 所有操作和查询涉及到的行编号和列编号都不超过 n 。

HINT
\(1\leq m\leq 10^4,1\leq a,b,k \leq 4\times10^6, 0 ≤ x < 10^{18}\)

Solution

观察一下可以发现,对于 \(d=gcd(i,j),a_{i,j}=f(d,d)\times\frac{i}{d}\times\frac{j}{d}\) .

每次修改只要修改 \(f(gcd(i,j),gcd(i,j))\) 的格子.

每次询问就转化成求 \(\sum_{j=1}^kf(d,d)\times\frac{i\times j}{d^2}\).

\(d\) 提取出来得到
\(\begin{split}&\sum_{d=1}^{k}f(d,d)\sum_{i=1}^{\lfloor \frac{k}{d}\rfloor}\sum_{j=1}^{\lfloor \frac{k}{d}\rfloor}i\times j\\=&\sum_{d=1}^{k}f(d,d)[\sum_{i=2}^{\lfloor \frac{k}{d}\rfloor}(i\times2\times\sum_{j=1}^{i}\sum_{gcd(i,j)=1}j)+1]\\=&\sum_{d=1}^{k}f(d,d)[\sum_{i=2}^{\lfloor \frac{k}{d}\rfloor}(i\times 2\times\frac{i\times\phi(i)}{2})+1]\\=&\sum_{d=1}^{k}f(d,d)[\sum_{i=2}^{\lfloor \frac{k}{d}\rfloor}(i^2\times\phi(i))+1]\end{split}\)

\(i^2\times\phi(i)\) 可以前缀和预处理 , \(\lfloor \frac{k}{d}\rfloor\)\(O(\sqrt{k})\).

对于 \(f(d,d)[\sum_{i=2}^{\lfloor \frac{k}{d}\rfloor}(i^2\times\phi(i))+1]\) 前缀和分块, \(O(\sqrt{n})\)修改, \(O(1)\) 询问.

#define K 2001
#define P 300000
#define N 4000001
#define M 1000000007
int f[N],s[N],fa[N],sum[K],tot[N],phi[N],p[P],inv[N],n,m,cnt,siz,ans;
bool b[N];
inline void prime(int n){
	phi[1]=1;
	for(int i=2;i<=n;++i){
		if(!b[i]) p[++cnt]=i,phi[i]=i-1;
		for(int j=1,k;j<=cnt&&(k=i*p[j])<=n;++j){
			b[k]=true;
			if(!(i%p[j])){
				phi[k]=p[j]*phi[i];break;
			}
			phi[k]=phi[i]*phi[p[j]];
		}
	}
}
inline void change(int x,int k){
	if(k<0) k+=M;
	f[x]+=k;
	if(f[x]>=M) f[x]-=M;
	for(int i=fa[x]+1;i<=cnt;++i){
		sum[i]+=k;
		if(sum[i]>=M) sum[i]-=M;
	}
	for(int i=min(fa[x]*siz,n);i>=x;--i){
		tot[i]+=k;
		if(tot[i]>=M) tot[i]-=M;
	}
}
inline int pre(int n){
	return sum[fa[n]]+tot[n];
}
inline int gcd(int n,int m){
	int r=n%m;
	while(r){
		n=m;m=r;r=n%m;
	}
	return m;
}
inline int po(int x,int k){
	int ret=1;
	while(k){
		if(k&1) ret=1ll*ret*x%M;
		x=1ll*x*x%M;k>>=1;
	} 
	return ret;
}
inline void Aireen(){
	scanf("%d%d",&m,&n);
	prime(n);
	s[1]=1;
	for(int i=2;i<=n;++i)
		s[i]=1ll*phi[i]*i%M*i%M;
	for(int i=2;i<=n;++i){
		s[i]+=s[i-1];
		if(s[i]>=M) s[i]-=M;
	}
	for(int i=1;i<=n;++i) tot[i]=f[i]=1ll*i*i%M;
	//cnt块数,siz块大小 
	cnt=0;siz=sqrt(n);
	for(int i=1;i<=n;i+=siz){
		++cnt;
		for(int j=0;j<siz&&i+j<=n;++j){
			fa[i+j]=cnt;
			sum[cnt+1]+=f[i+j];
			if(sum[cnt+1]>=M) sum[cnt+1]-=M;
		}
	}
	for(int i=1;i<=cnt;++i){
		sum[i]+=sum[i-1];
		if(sum[i]>=M) sum[i]-=M; 
	}
	for(int i=1;i<=n;++i)
		if(fa[i]==fa[i-1]){
			tot[i]+=tot[i-1];
			if(tot[i]>=M) tot[i]-=M; 
		}
	int tmp=1;
	for(int i=2;i<=n;++i)
		tmp=1ll*tmp*i%M;
	inv[n]=po(tmp,M-2);
	inv[1]=1;
	for(int i=n-1;i>1;--i)
		inv[i]=1ll*(i+1)*inv[i+1]%M;
	tmp=1ll;
	for(int i=2;i<=n;++i){
		inv[i]=1ll*inv[i]*tmp%M;
		tmp=1ll*tmp*i%M;
	}
	int a,b,c,k;ll x;
	while(m--){
		scanf("%d%d%lld%d",&a,&b,&x,&k);
		c=gcd(a,b);
		change(c,1ll*x%M*inv[a/c]%M*inv[b/c]%M-f[c]);
		ans=0;
		for(int l=1,r=1;l<=k;l=r+1){
			r=k/(k/l);
			ans+=1ll*(pre(r)-pre(l-1))*s[k/l]%M;
			if(ans>=M) ans-=M;
			if(ans<0) ans+=M;
		}
		printf("%d\n",ans);
	}
}

day2

老C的任务

Description
老 C 是个程序员。
最近老 C 从老板那里接到了一个任务——给城市中的手机基站写个管理系统。 作为经验丰富的程序员,老 C 轻松地完成了系统的大部分功能,并把其中一个功能交给你来实现。
由于一个基站的面积相对于整个城市面积来说非常的小, 因此每个的基站都可以看作坐标系中的一个点,其位置可以用坐标(x,y)来表示。此外,每个基站还有很多属性,例如高度、功率等。运营商经常会划定一个区域,并查询区域中所有基站的信息。
现在你需要实现的功能就是,对于一个给定的矩形区域,回答该区域中( 包括区域边界上的) 所有基站的功率总和。 如果区域中没有任何基站,则回答 0。

HINT
\(-2^{31}\leq x_i,y_i\leq2^{31}\).

Solution
\(y\) 坐标排序 , 差分后树状数组维护即可.

#define N 100005
typedef long long ll;
struct point{
	int x,y,p;
}a[N];
struct Point{
	int x,y,k,n;
}b[N*4];
ll ans[N],s[N];
int p[N],n,m,k;
inline int read(){
	int ret=0,f=0;char c=getchar();
	while(!isdigit(c)){
		if(c=='-') f=1;
		c=getchar();
	}
	while(isdigit(c)){
		ret=(ret<<1)+(ret<<3)+c-'0';
		c=getchar();
	}
	if(f) return -ret;
	return ret;
}
bool operator < (point a,point b){
	return a.y<b.y;
}
bool operator < (Point a,Point b){
	return a.y<b.y;
}
//the first <=
inline int search(int x){
	if(x<p[1]) return 0; 
	int l=1,r=k,mid;
	while(l<r){
		mid=(l+r+1)>>1;
		if(p[mid]<=x) l=mid;
		else r=mid-1;
	}
	return l;
}
inline int lowbit(int x){
	return x&(-x);
}
inline void add(int x,ll y,int n){
	for(int i=x;i<=n;i+=lowbit(i))
		s[i]+=y;
	
}
inline ll ask(int x){
	ll ret=0ll;
	for(int i=x;i;i-=lowbit(i))
		ret+=s[i];
	return ret;
}
inline void Aireen(){
	n=read();m=read();
	for(int i=1;i<=n;++i){
		a[i].x=read();a[i].y=read();a[i].p=read();p[i]=a[i].x;
	}
	sort(p+1,p+1+n);
	k=unique(p+1,p+1+n)-p-1;
	for(int i=1,j=1;i<=m;++i,j+=4){
		b[j].x=read()-1;b[j].y=read()-1;
		b[j+1].x=read();b[j+1].y=read();
		b[j+2].x=b[j].x;b[j+2].y=b[j+1].y;
		b[j+3].x=b[j+1].x;b[j+3].y=b[j].y;
		b[j].n=b[j+1].n=b[j+2].n=b[j+3].n=i; 
		b[j].k=b[j+1].k=1;b[j+2].k=b[j+3].k=-1;
	}
	m<<=2;
	sort(a+1,a+1+n);
	sort(b+1,b+1+m);
	int j=1; 
	for(int i=1;i<=n;++i){
		for(;j<=m&&b[j].y<a[i].y;++j){
			if(b[j].k>0) ans[b[j].n]+=ask(search(b[j].x));
			else ans[b[j].n]-=ask(search(b[j].x));
		}
		add(search(a[i].x),1ll*a[i].p,k);
	}
	for(;j<=m;++j){
		if(b[j].k>0) ans[b[j].n]+=ask(search(b[j].x));
		else ans[b[j].n]-=ask(search(b[j].x));
	}
	m>>=2;
	for(int i=1;i<=m;++i)
		printf("%lld\n",ans[i]);
}

老C的方块

Description
老 C 是个程序员。
作为一个懒惰的程序员,老 C 经常在电脑上玩方块游戏消磨时间。

游戏被限定在一个由小方格排成的 R 行 C 列网格上,如果两个小方格有公共的边,就称它们是相邻的,而且有些相邻的小方格之间的公共边比较特殊。 特殊的公共边排列得有很强的规律。首先规定,第1行的前两个小方格之间的边是特殊边。然后,特殊边在水平方向上每4个小方格为一个周期,在竖直方向上每2个小方格为一个周期。所有的奇数列与下一列之间都有特殊边,且所在行的编号从左到右奇偶交替。

网格的每个小方格刚好可以放入一个小方块,在游戏的一开始,有些小方格已经放上了小方块,另外的小方格没有放。老 C 很讨厌下图所示的图形,如果他发现有一些小方块排列成了它讨厌的形状(特殊边的位置也要如图中所示),就很容易弃疗,即使是经过任意次旋转、翻转后排列成讨厌的形状,老 C 也同样容易弃疗。

为了防止弃疗,老 C 决定趁自己还没有弃疗,赶紧移除一些格子里小方块,使得剩下的小方块不能构成它讨厌的形状。但是游戏里每移除一个方块都是要花费一些金币的,每个方块需要花费的金币有多有少参差不齐。老 C 当然希望尽可能少的使用游戏里的金币,但是最少要花费多少金币呢?老 C 懒得思考,就把这个问题交给你了。

HINT
\(1\leq C,R,n\leq10^5,1\leq w\leq 10^4\).

Solution
观察图形可以发现 , 讨厌的形状都是4个格子 , 并且特殊边两边各有2个格子 .

所以考虑染色 ,

一个讨厌的形状就是粉->蓝->黄->紫.

每个格子拆点,按费用建个图,最小割即可.

#define N 100005
#define M 1200005
#define INF 1000000000
struct block{
	int y,x,w,n;
}a[N];
struct graph{
	int nxt,to,f;
}e[M];
int g[N<<1],dep[N<<1],n,s,t,c,r,cnt=1;
queue<int> q;
inline int read(){
	int ret=0;char c=getchar();
	while(!isdigit(c))
		c=getchar();
	while(isdigit(c)){
		ret=(ret<<1)+(ret<<3)+c-'0';
		c=getchar();
	}
	return ret;
}
inline void addedge(int x,int y,int f){
	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f;
}
inline void adde(int x,int y,int f){
	addedge(x,y,f);addedge(y,x,0);
}
inline bool cmpx(block a,block b){
	if(a.x!=b.x) return a.x<b.x;
	return a.y<b.y;
}
inline bool cmpy(block a,block b){
	if(a.y!=b.y) return a.y<b.y;
	return a.x<b.x;
}
#define pink 0
#define blue 1
#define yellow 2
#define purple 3
inline int color(int x,int y){
	int u=x%2,v=y%4;
	if((u==1&&v==0)||(u==0&&v==1))
		return pink;
	if((u==1&&v==1)||(u==0&&v==0))
		return blue;
	if((u==1&&v==2)||(u==0&&v==3))
		return yellow;
	return purple;
}
inline bool bfs(int u){
	memset(dep,0,sizeof(dep));
	q.push(u);dep[u]=1;
	while(!q.empty()){
		u=q.front();q.pop();
		for(int i=g[u];i;i=e[i].nxt){
			if(e[i].f>0&&!dep[e[i].to]){
				q.push(e[i].to);
				dep[e[i].to]=dep[u]+1;
			}
		}
	}
	return dep[t];
}
inline int dfs(int u,int f){
	int ret=0;
	if(u==t) return f;
	for(int i=g[u],d;i&&f;i=e[i].nxt)
		if(e[i].f>0&&dep[e[i].to]>dep[u]){
			d=dfs(e[i].to,min(e[i].f,f));
			ret+=d;f-=d;e[i].f-=d;e[i^1].f+=d;
		}
	if(!ret) dep[u]=-1;
	return ret;
}
inline int dinic(){
	int ret=0;
	while(bfs(s)){
		ret+=dfs(s,INF);
	}
	return ret;
}
inline void Aireen(){
	c=read();r=read();n=read();
	for(int i=1;i<=n;++i)
		a[i]=(block){read(),read(),read(),i};
	s=n<<1|1;t=s+1;
	for(int i=1;i<=n;++i){
		adde(a[i].n,a[i].n+n,a[i].w);
		if(color(a[i].x,a[i].y)==pink){
			adde(s,a[i].n,INF);
		}
		else if(color(a[i].x,a[i].y)==purple)
			adde(a[i].n+n,t,INF);
	}
	sort(a+1,a+1+n,cmpx);
	for(int i=1;i<=n;++i)
		if(color(a[i].x,a[i].y)==pink){
			if(i>1&&color(a[i-1].x,a[i-1].y)==blue&&a[i].x==a[i-1].x&&a[i].y-1==a[i-1].y)
				adde(a[i].n+n,a[i-1].n,INF);
			if(i<n&&color(a[i+1].x,a[i+1].y)==blue&&a[i].x==a[i+1].x&&a[i].y+1==a[i+1].y)
				adde(a[i].n+n,a[i+1].n,INF);
		}
		else if(color(a[i].x,a[i].y)==blue){
			if(i>1&&color(a[i-1].x,a[i-1].y)==yellow&&a[i].x==a[i-1].x&&a[i].y-1==a[i-1].y)
				adde(a[i].n+n,a[i-1].n,INF);
			if(i<n&&color(a[i+1].x,a[i+1].y)==yellow&&a[i].x==a[i+1].x&&a[i].y+1==a[i+1].y)
				adde(a[i].n+n,a[i+1].n,INF);
		}
		else if(color(a[i].x,a[i].y)==yellow){
			if(i>1&&color(a[i-1].x,a[i-1].y)==purple&&a[i].x==a[i-1].x&&a[i].y-1==a[i-1].y)
				adde(a[i].n+n,a[i-1].n,INF);
			if(i<n&&color(a[i+1].x,a[i+1].y)==purple&&a[i].x==a[i+1].x&&a[i].y+1==a[i+1].y)
				adde(a[i].n+n,a[i+1].n,INF);
		}
	sort(a+1,a+1+n,cmpy);
	for(int i=1;i<=n;++i)
		if(color(a[i].x,a[i].y)==pink){
			if(i>1&&color(a[i-1].x,a[i-1].y)==blue&&a[i].y==a[i-1].y&&a[i].x-1==a[i-1].x)
				adde(a[i].n+n,a[i-1].n,INF);
			if(i<n&&color(a[i+1].x,a[i+1].y)==blue&&a[i].y==a[i+1].y&&a[i].x+1==a[i+1].x)
				adde(a[i].n+n,a[i+1].n,INF);
		}
		else if(color(a[i].x,a[i].y)==blue){
			if(i>1&&color(a[i-1].x,a[i-1].y)==yellow&&a[i].y==a[i-1].y&&a[i].x-1==a[i-1].x)
				adde(a[i].n+n,a[i-1].n,INF);
			if(i<n&&color(a[i+1].x,a[i+1].y)==yellow&&a[i].y==a[i+1].y&&a[i].x+1==a[i+1].x)
				adde(a[i].n+n,a[i+1].n,INF);
		}
		else if(color(a[i].x,a[i].y)==yellow){
			if(i>1&&color(a[i-1].x,a[i-1].y)==purple&&a[i].y==a[i-1].y&&a[i].x-1==a[i-1].x)
				adde(a[i].n+n,a[i-1].n,INF);
			if(i<n&&color(a[i+1].x,a[i+1].y)==purple&&a[i].y==a[i+1].y&&a[i].x+1==a[i+1].x)
				adde(a[i].n+n,a[i+1].n,INF);
		}
	printf("%d\n",dinic());
}

2017-04-24 12:02:20

posted @ 2021-11-25 23:16  Aireen_Ye  阅读(7)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.