2018.11.02 练习赛

T1 【NOIP2018模拟1102】A

题意:

给一条直线解析式\(y=-\frac{x}{m}+b\),求直线一整点\((x,y)\)与原点围成矩形中所有整点横纵坐标之和;\((m<=1000,b<=1000)\);

题解:

很容易得出式子:

\(Ans=max\{\sum_{i=0}^{x} \sum_{j=0}^{y} {(i+j)}\}\)

\(=max\{\frac{(x+y)(x+1)(y+1)}{2}\}\)

\(y\)带进去后发现是一个仅与\(x\)有关的三次函数,由于取整点,不具有三分性质;

注意到\(m,b\)较小,\(x\in[0,m*b]\),直接枚举x即可;

\(code\):

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;

ll m,b,ans;
ll sum[1000002];
int main()
{
	scanf("%lld%lld",&m,&b);
	for(ll i=1;i<=b;i++) sum[i]=sum[i-1]+i;
	for(ll i=0;i<=b;i++)
	{
		ll y=(b-i)*m,sx,mx;
		sx=sum[i];
		mx=sum[i]+y*(i+1);
		ans=max(ans,(sx+mx)*(y+1)/2);
	}
	printf("%lld\n",ans);
}

T2【NOIP2018模拟1102】B

题面:

对初始为\(0\)\(0/1\)序列完成两种操作:

1.查询是否存在长度为\(len\)的序列全为\(0\),如存在输出最左端位置,并将这段区间置\(0\);

2.将\([l,l+r-1]\)区间置\(0\);

线段树维护三个值:\(lmx,rmx,mx\);

意为左端起最长连续\(0\)个数,右端最长连续\(0\)个数,维护区间内最长连续\(0\)个数;

一合并左儿子为例合并区间:

看左儿子的\(lmx\)是否等于整个左区间长度,如果是将本节点的\(lmx\)更新为\(lmx[ls]+lmx[rs];\)

否则更新为\(lmx[ls];\)

考虑更新\(mx\),则从\(mx[ls],mx[rs],rmx[ls]+lmx[rs]\)中取最大值;

\(code:\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<ctype.h>
using namespace std;

char buf[1<<20],*p1,*p2;
inline char gc()
{
//	return getchar();
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}

template<typename T>
inline void read(T &x)
{
	char tt;
	while(!isdigit(tt=gc()));x=(tt^'0');
	while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
}

const int maxn=50002;
int n,m;
int mx[maxn<<2],lmx[maxn<<2],rmx[maxn<<2],lazy[maxn<<2];
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)

void update(int p,int l,int r)
{
	if(lmx[ls]==mid-l+1)
	lmx[p]=lmx[ls]+lmx[rs];
	else lmx[p]=lmx[ls];
	
	if(rmx[rs]==r-(mid+1)+1)
	rmx[p]=rmx[rs]+rmx[ls];
	else rmx[p]=rmx[rs];
	
	mx[p]=max(mx[ls],mx[rs]);
	mx[p]=max(mx[p],rmx[ls]+lmx[rs]);
}

void pushdown(int p,int l,int r)
{
	if(l==r) {lazy[p]=0;return;}
	lazy[ls]=lazy[rs]=lazy[p];
	if(lazy[p]==1)//yi
	{
		lmx[ls]=rmx[ls]=mx[ls]=0;
		lmx[rs]=rmx[rs]=mx[rs]=0;
		lazy[p]=0;
	}
	else//ling
	{
		lmx[ls]=rmx[ls]=mx[ls]=mid-l+1;
		lmx[rs]=rmx[rs]=mx[rs]=r-(mid+1)+1;
		lazy[p]=0;
	}
}

void maketree(int p,int l,int r)
{
	if(l==r)
	{
		mx[p]=lmx[p]=rmx[p]=1;
		return;
	}
	maketree(ls,l,mid);
	maketree(rs,mid+1,r);
	update(p,l,r);
}

void modify(int p,int l,int r,int x,int y,int id)
{
	if(x<=l&&y>=r)
	{
		if(id==1)//变一 
		{
			lazy[p]=1;
			mx[p]=lmx[p]=rmx[p]=0;
		}
		else//变零 
		{
			lazy[p]=2;
			mx[p]=lmx[p]=rmx[p]=r-l+1;
		}
		return;
	}
	if(lazy[p]) pushdown(p,l,r);
	if(x<=mid) modify(ls,l,mid,x,y,id);
	if( y>mid) modify(rs,mid+1,r,x,y,id);
	update(p,l,r);
}

int query(int p,int l,int r,int d)
{
	if(lmx[p]>=d) return l;
	if(mx[p]<d) return 0;
	if(lazy[p]) pushdown(p,l,r);
	if(mx[ls]>=d) return query(ls,l,mid,d);
	if(rmx[ls]>=1&&rmx[ls]+lmx[rs]>=d) 
	return mid-rmx[ls]+1;
	return query(rs,mid+1,r,d);
}

void solve(int x)
{
	int t=query(1,1,n,x);
	if(t) modify(1,1,n,t,t+x-1,1);
	printf("%d\n",t);
}

int main()
{
	read(n);read(m);
	maketree(1,1,n);
	while(m--)
	{
		int opt,x,y;
		read(opt);
		if(opt==1)
		{
			read(x);
			solve(x);
		}
		else
		{
			read(x),read(y);
			modify(1,1,n,x,x+y-1,2);
		}
	}
	return 0;
}

T3 【NOIP2018模拟1102】C

题意:

给几个仙人掌,让你找两点间的路径数;

题解:

随便手模以下,发现当在同一个环上时,路径数为\(2\),当两点间没有环时为\(1\),然后一个显然的结论可推出,答案为\(2^{两点间环数}\);

\(tarjan\)缩点,重新构图(你会圆方树也行=_=),然后原图就变成一个森林(题目没保证联通),跑倍增记下环数,\(f[x][0]=sz[x]>1\),记录根为哪个节点判不连通就方便;

(话说以后还是少用\(f\)当倍增数组啊,调半天才发现是求答案时把\(f\)写成\(fa\)了=_=)

\(code:\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<stack>
#include<ctype.h>
#include<vector>
#define ll long long
using namespace std;

char buf[1<<20],*p1,*p2;
inline char gc()
{
//	return getchar();
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}

template<typename T>
inline void read(T &x)
{
	char tt;
	while(!isdigit(tt=gc()));x=(tt^'0');
	while(isdigit(tt=gc())) x=(x<<1)+(x<<3)+(tt^'0');
}
const int maxn=100002;
const int mod=1e9+7;
int n,m,sz[maxn],tot,q;
int dfn[maxn],low[maxn],root[maxn],scc,belong[maxn];
bool instack[maxn];
int fa[maxn][20],log_[maxn],f[maxn][20],dep[maxn];
int ans[maxn];
vector<int>g[maxn];
vector<int>G[maxn];
stack<int>s;

inline int mul(int x){return (x<<1)<mod?(x<<1):(x<<1)-mod;}

void dfs(int x,int pre)
{
	low[x]=dfn[x]=++tot;
	s.push(x);instack[x]=1;
	int p;
	for(int i=G[x].size()-1;i>=0;i--)
	{
		p=G[x][i];
		if(p==pre) continue;
		if(!dfn[p])
		{
			dfs(p,x);
			low[x]=min(low[x],low[p]);
		}
		else if(dfn[p]&&instack[p])
		low[x]=min(low[x],low[p]);
	}
	if(dfn[x]==low[x])
	{
		scc++;
		do{
			p=s.top();s.pop();
			instack[p]=0;sz[scc]++;
			belong[p]=scc;
		}while(x!=p);
	}
}

void dfs(int x,int pre,int t)
{
	root[x]=t;dep[x]=dep[pre]+1;
	fa[x][0]=pre;f[x][0]=sz[x]>1;
	for(int i=1;i<=log_[scc];i++)
	if(fa[x][i-1])
	fa[x][i]=fa[fa[x][i-1]][i-1],
	f[x][i]=f[fa[x][i-1]][i-1]+f[x][i-1];
	else break;
	
	for(int i=g[x].size()-1;i>=0;i--)
	{
		int p=g[x][i];
		if(p==pre) continue;
		dfs(p,x,t);
	}
}

int query(int x,int y)
{
	int ret=0;
	if(x==y) return f[x][0];
	if(dep[x]<dep[y]) swap(x,y);
	int del=dep[x]-dep[y];
	for(int i=0;i<=log_[n];i++)
	if(del>>i&1)
	ret+=f[x][i],
	x=fa[x][i];
	if(x==y) return ret+f[x][0];
	
	for(int i=log_[scc];i>=0;i--)
	if(fa[x][i]^fa[y][i])
	ret+=f[x][i],ret+=f[y][i],
	x=fa[x][i],y=fa[y][i];
	
	return ret+f[fa[x][0]][0]+f[x][0]+f[y][0];
}

int main()
{
	read(n),read(m);
	log_[0]=-1;ans[0]=1;
	for(int i=1;i<=n;i++)
	log_[i]=log_[i>>1]+1,ans[i]=mul(ans[i-1]);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		read(x),read(y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	for(int i=1;i<=n;i++)
	if(!dfn[i]) dfs(i,0);
	
	for(int x=1;x<=n;x++)
	for(int i=G[x].size()-1;i>=0;i--)
	{
		int p=G[x][i];
		if(belong[x]==belong[p]) continue;
		g[belong[x]].push_back(belong[p]);
	}read(q);
	for(int i=1;i<=scc;i++)
	if(!root[i]) dfs(i,0,i);
	while(q--)
	{
		int x,y;
		read(x),read(y);
		x=belong[x],y=belong[y];
		if(root[x]^root[y]) {puts("0");continue;}
		printf("%d\n",ans[query(x,y)]);
	}
}
posted @ 2018-11-03 22:52  Katoumegumi  阅读(61)  评论(0编辑  收藏  举报
返回顶部