CQOI2017 部分题解

部分题解是指没写那道算几。

BZOJ上目前没有day2的题面D2T2的图。

BZOJ4813 小Q的棋盘

显然可以$O(n^2)$DP,然而可以$O(n)$贪心:只有一条从根出发的一条链上的边可以只经过一次,其他边如果被经过则必须经过两次,且次数没用完时一定可以用来经过没被经过的边,那么只用枚举那条链的端点。

感觉和SCOI D1T1神似:$O(n^2)$树形DP显然,然而可以$O(n)$贪心。只不过SCOI DP过不了

#include<stdio.h>
struct node;
typedef struct node edge;
struct node{
	int v;
	edge*s;
}e[198];
edge*l=e,*h[100];
void ins(int u,int v){
	edge s={v,h[u]};
	*(h[u]=l++)=s;
}
int n,m,u,v,a,d[100];
void dfs(int u,int j){
	if(m>=d[u]){
		if(a<d[u]+m+2>>1)
			a=d[u]+m+2>>1;
		for(edge*i=h[u];i;i=i->s)
			if(i->v!=j)
				d[i->v]=d[u]+1,dfs(i->v,u);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;++i)
		scanf("%d%d",&u,&v),ins(u,v),ins(v,u);
	dfs(0,0);
	printf("%d\n",a<n?a:n);
}

BZOJ4815 小Q的表格

设$\gcd(a,b)=d$,则$f(a,b)=\frac{ab}{d^2}f(d,d)$。

\begin{align*}&\sum_{d=1}^nf(d,d)\sum_{i=1}^{\left\lfloor\frac nd\right\rfloor}\sum_{j=1}^{\left\lfloor\frac nd\right\rfloor}ij[(i,j)=1]\\=&\sum_{d=1}^nf(d,d)\sum_{i=1}^{\left\lfloor\frac nd\right\rfloor}i^2\varphi(i)\end{align*}

分块维护一下$f(d,d)$就好了,复杂度$O(n+m\sqrt{n})$。

#include<cstdio>
#include<cmath>
typedef unsigned long long ll;
const int p=1e9+7;
const int N=4e6+5;
int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}
inline int foo(int x){
	return x<p?x:x-p;
}
inline int bar(int x){
	return x<0?x+p:x;
}
inline void inc(int&x,int y){
	x=foo(x+y);
}
typedef int arr[N];
int m,n,nb,bs,np;
arr b,c1,c2,c3,c4,f,q;
inline int ask(int x){
	return x>bs?foo(c3[x]+c4[b[x]]):c3[x];
}
void cov(int x,int y){
	int d=bar(y-bar(ask(x)-ask(x-1)));
	for(int i=x;i<=c2[b[x]];++i)
		inc(c3[i],d);
	for(int i=b[x]+1;i<=nb-1;++i)
		inc(c4[i],d);
}
int main(){
	scanf("%d%d",&m,&n);
	bs=sqrt(n+.5)*3/2;
	for(int i=1;i<=n;++i)
		b[i]=(i-1)/bs;
	nb=b[n]+1;
	for(int i=1;i<=n;++i)
		c2[b[i]]=i;
	for(int i=n;i>=1;--i)
		c1[b[i]]=i;
	for(int i=1;i<=n;++i){
		c3[i]=(ll)i*i%p;
		inc(c4[b[i]+1],c3[i]);
		if(i!=c1[b[i]])
			inc(c3[i],c3[i-1]);
	}
	for(int i=1;i<nb;++i)
		inc(c4[i],c4[i-1]);
	f[1]=1;
	for(int i=2;i<=n;++i){
		if(!f[i]){
			f[i]=(ll)i*i%p*(i-1)%p;
			q[np++]=i;
		}
		for(int*j=q;;++j){
			if(i**j>n)break;
			if(i%*j==0){
				f[i**j]=(ll)*j**j**j%p*f[i]%p;
				break;
			}
			f[i**j]=(ll)f[i]*f[*j]%p;
		}
	}
	for(int i=2;i<=n;++i)
		inc(f[i],f[i-1]);
	while(m--){
		int x,y;
		ll z;
		scanf("%d%d%lld%d",&x,&y,&z,&n);
		int d=gcd(x,y);
		cov(d,z/(x/d)/(y/d)%p);
		int i=1,s=0;
		while(i<=n){
			int j=n/(n/i);
			s=(s+(ll)bar(f[j]-f[i-1])*ask(n/i))%p;
			i=j+1;
		}
		printf("%d\n",s);
	}
}

BZOJ4822 老C的任务

签到题。

和SCOI D2T1神似:NOIP难度以下签到题。只不过SCOI有人被卡常数T了5分

#include<bits/stdc++.h>
#define lb lower_bound
#define ub upper_bound
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1e5+5;
struct node{
	node*i,*j;
	ll s;
}e[N*20];
node*l=e,*r[N];
void ins(int z,int y,node**o){
	for(int i=16;~i;--i){
		*++l=**o,*o=l;
		if(y>>i&1)o=&(*o)->j;
		else
			(*o)->s+=z,o=&(*o)->i;
	}
}
ll ask(int y,node*s,node*t){
	ll z=0;
	for(int i=16;~i;--i)
		if(~y>>i&1)s=s->i,t=t->i;
		else
			z+=t->s-s->s,s=s->j,t=t->j;
	return z;
}
int n,m,s,t,u,v,x[N],y[N],z[N],a[N],b[N];
vector<int>c[N];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;++i)
		scanf("%d%d%d",x+i,y+i,z+i),a[i]=x[i],b[i]=y[i];
	sort(a,a+n);
	sort(b,b+n);
	for(int i=0;i<n;++i)
		c[lb(a,a+n,x[i])-a+1].pb(i),y[i]=lb(b,b+n,y[i])-b+1;
	*(*r=e)=(node){e,e};
	for(int i=1;i<=n;++i){
		r[i]=r[i-1];
		for(int j=0;j<c[i].size();++j){
			int k=c[i][j];
			ins(z[k],y[k],r+i);
		}
	}
	while(m--){
		scanf("%d%d%d%d",&s,&t,&u,&v);
		s=lb(a,a+n,s)-a+1;
		t=lb(b,b+n,t)-b+1;
		u=ub(a,a+n,u)-a;
		v=ub(b,b+n,v)-b;
		printf("%lld\n",ask(v+1,r[s-1],r[u])-ask(t,r[s-1],r[u]));
	}
}

BZOJ4823 老C的方块

设特殊边两边的方块为灰色,这两个方块的其他相邻方块分别为黑色和白色,可以将整个棋盘染色。当两个灰色方块同时存在时,相邻的其他方块只能有一种颜色,那么容易建立最小割模型,S向所有黑色方块连边,所有白色方块向T连边,容量为权值;灰色方块与相邻的黑色或白色方块连边,容量为无穷大;两个灰色方块之间连边,容量为两个方块权值的较小值。

#include<bits/stdc++.h>
using namespace std;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int N=1e5+5;
const int inf=1061109567;
struct edge{
	int v,c;
	edge*s;
}e[N*8];
edge*l=e,*h1[N],*h2[N];
void ins(int u,int v,int c){
	edge a={v,c,h1[u]};
	edge b={u,0,h1[v]};
	*(h1[u]=l++)=a;
	*(h1[v]=l++)=b;
}
int s1,s2,d[N],q[N];
int dfs(int u,int c){
	if(u==s2)return c;
	int f=0;
	for(edge*&i=h2[u];i;i=i->s)
		if(d[u]+1==d[i->v]&&i->c){
			int v=dfs(i->v,min(c-f,i->c));
			i->c-=v;
			e[i-e^1].c+=v;
			f+=v;
			if(f==c)break;
		}
	if(!f)d[u]=-1;
	return f;
}
int bfs(){
	int f=0;
	for(;;f+=dfs(s1,inf)){
		fill(d,d+s2+1,-1);
		d[q[0]=s1]=0;
		for(int a=0,b=0;a<=b;++a){
			int u=q[a];
			for(edge*i=h2[u]=h1[u];i;i=i->s)
				if(!~d[i->v]&&i->c)
					d[q[++b]=i->v]=d[u]+1;
		}
		if(!~d[s2])return f;
	}
}
int jud(int x,int y){
	int a=x&1,b=y&3;
	return a&&!b||!a&&b==1?0:a&&b==3||!a&&b==2?1:a&&b==1||!a&&!b?2:3;
}
map<int,int>f[N];
int n,x[N],y[N],w[N];
int main(){
	scanf("%*d%*d%d",&n),s1=n,s2=n+1;
	for(int i=0;i<n;++i){
		scanf("%d%d%d",y+i,x+i,w+i);
		f[x[i]][y[i]]=i;
	}
	for(int i=0;i<n;++i){
		int x=::x[i],y=::y[i];
		if(!jud(x,y))
			ins(s1,i,w[i]);
		else if(jud(x,y)==1)
			ins(i,s2,w[i]);
		else
			for(int j=0;j<4;++j){
				int nx=x+dx[j],ny=y+dy[j];
				int z=jud(nx,ny);
				map<int,int>::iterator k=f[nx].find(ny);
				if(k!=f[nx].end())
					if(!z)
						ins(k->second,i,inf);
					else if(z==1)
						ins(i,k->second,inf);
					else if(z==3)
						ins(i,k->second,min(w[i],w[k->second]));
			}
	}
	printf("%d\n",bfs());
}

BZOJ4824 老C的键盘

把BZOJ3167粘过来改改就好了。

#include<algorithm>
#include<cstdio>
using namespace std;
typedef unsigned long long ll;
const int p=1e9+7;
const int N=100;
ll f[N][N],g[N],c[N][N];
int n,e[N][N],r[N];
void dfs(int u,int l){
	r[u]=f[u][0]=1;
	for(int v=0;v<n;++v)
		if(e[u][v]&&v!=l){
			dfs(v,u);
			fill(g,g+n,0);
			for(int i=0;i<r[u];++i)
				if(~e[u][v])
					for(int j=1;j<=r[v];++j)
						(g[i+j]+=f[u][i]*f[v][j-1]%p*c[i+j][j]%p*c[r[u]+r[v]-i-j-1][r[v]-j])%=p;
				else
					for(int j=0;j<r[v];++j)
						(g[i+j]+=f[u][i]*f[v][j]%p*c[i+j][j]%p*c[r[u]+r[v]-i-j-1][r[v]-j])%=p;
			copy(g,g+n,f[u]);
			r[u]+=r[v];
		}
	if(~e[u][l])
		for(int i=r[u]-2;~i;--i)
			(f[u][i]+=f[u][i+1])%=p;
	else
		for(int i=1;i<r[u];++i)
			(f[u][i]+=f[u][i-1])%=p;
}
char w[N];
int main(){
	for(int i=0;i<N;++i){
		c[i][0]=1;
		for(int j=1;j<N;++j)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
	}
	scanf("%d%s",&n,w);
	for(int i=1;i<n;++i){
		int u=(i+1)/2-1;
		if(w[i-1]=='<')
			e[i][u]=1,e[u][i]=-1;
		else
			e[u][i]=1,e[i][u]=-1;
	}
	dfs(0,0);
	printf("%d\n",f[0][0]);
}

转载于:https://www.cnblogs.com/f321dd/p/6716924.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值