2020/9/13 NOIP模拟赛

Preface

被陈指导带到2hAK的一场

括号序列

题目大意:给(n)个形如()cdots)(cdots()的字符串((x)个右括号(y)个左括号),求将它们连接起来得到一个括号序列

最少删除多少个括号可以使得这个括号序列合法

解法:讲每个序列分类,设左括号比右括号多的为类型(1),否则为类型(2)

显然类型(1)的括号要排在类型(2)的括号之前

然后对于同为类型(1)的括号,显然右括号少的排在前面;对于同为类型(2)的括号,显然左括号多的排在前面

#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
struct element
{
	int a,b;
	friend inline bool operator < (const element& A,const element& B)
	{
		int ta=A.a>A.b,tb=B.a>B.b; if (ta!=tb) return ta<tb;
		if (ta) return A.b>B.b; else return A.a<B.a;
	}
}t[N]; int n; long long ret,ans;
int main()
{
	freopen("brackets.in","r",stdin); freopen("brackets.out","w",stdout);
	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&t[i].a,&t[i].b);
	for (sort(t+1,t+n+1),i=1;i<=n;++i)
	{
		if (ret-=t[i].a,ret<0) ans+=-ret,ret=0; ret+=t[i].b;
	}
	return printf("%lld",ans+ret),0;
}

大森林

题目大意:定义边权为颜色的树上一条路径的贡献为这条路径上的颜色数,求所有点对的贡献和

解法:经典的拆贡献+容斥的做法,枚举每一条边考虑贡献

显然我们可以吧所有这种颜色的边断开,那么所有无法互相到达的点对都是要产生贡献的

容斥一下就是总点数减去所有联通块大小的平方,因此我们考虑如何维护联通块的大小

很显然可以按深度从大到小删除这种颜色的边,然后用树状数组维护子树的(size)即可

但是显然我们发现一条颜色的边只会向上对同颜色的边的子节点造成贡献,因此可以直接DP(陈指导的做法)

这里懒了就直接贴(O(nlog n))的了

#include<cstdio>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=500005;
struct edge
{
	int to,nxt,c;
}e[N<<1]; int n,head[N],cnt,x,y,z,size[N],L[N],R[N],idx;
struct element
{
	int p,dep;
	friend inline bool operator < (const element& A,const element& B)
	{
		return A.dep>B.dep;
	}
}; vector <element> c[N]; long long ans;
inline void addedge(CI x,CI y,CI z)
{
	e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
	e[++cnt]=(edge){x,head[y],z}; head[y]=cnt;
}
class Tree_Array
{
	private:
		int bit[N],stk[N],val[N],top;
	public:
		#define lowbit(x) (x&-x)
		inline int get(RI x,int ret=0)
		{
			for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
		}
		inline void add(RI x,CI y,const bool& t=1)
		{
			if (t) stk[++top]=x,val[top]=y; for (;x<=n;x+=lowbit(x)) bit[x]+=y;
		}
		inline void revoke(void)
		{
			while (top) add(stk[top],-val[top],0),--top;
		}
		#undef lowbit
}BIT;
#define to e[i].to
inline void DFS(CI now=1,CI fa=0,CI dep=1)
{
	L[now]=++idx; size[now]=1; for (RI i=head[now];i;i=e[i].nxt)
	if (to!=fa) c[e[i].c].push_back((element){to,dep+1}),
	DFS(to,now,dep+1),size[now]+=size[to]; R[now]=idx;
}
#undef to
int main()
{
	freopen("forest.in","r",stdin); freopen("forest.out","w",stdout);
	RI i; for (scanf("%d",&n),i=1;i<n;++i)
	scanf("%d%d%d",&x,&y,&z),addedge(x,y,z);
	for (DFS(),i=1;i<=n;++i) if (c[i].size())
	{
		sort(c[i].begin(),c[i].end()); long long ret=1LL*n*n; int tp;
		for (vector <element>:: iterator it=c[i].begin();it!=c[i].end();++it)
		tp=size[it->p]-(BIT.get(R[it->p])-BIT.get(L[it->p])),ret-=1LL*tp*tp,BIT.add(L[it->p],tp);
		tp=size[1]-BIT.get(n); ret-=1LL*tp*tp; BIT.revoke(); ans+=ret;
	}
	return printf("%lld",ans),0;
}

魔法

题目大意:在一个(S imes S)的矩形上(有(n)个障碍)选出两个(L)形,满足两个(L)形的边长的互质且不重合且不碰到障碍就是一种合法方案,求合法的总方案数

解法:考虑从右下向左上枚举(L)形的拐点,显然此时只需要考虑前一个(L)形对当前的占用的影响,显然此时只有同一行或者在左下角挡住当前的向下延伸的情况:

  • 同一行:对行差分累计答案
  • 左下角:对每一列打上标记,同样可以通过差分统计

其它情况显然都是任取的,因此就做完了,复杂度(O(n^3))

#include<cstdio>
#include<cstring>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=205;
struct element
{
	int x,y;
	friend inline bool operator < (const element& A,const element& B)
	{
		return A.x!=B.x?A.x<B.x:A.y<B.y;
	}
}s[N*N]; int n,f[N][N],g[N][N],r[N][N],d[N][N],tag[N][N],cur,ret; long long ans;
inline int gcd(CI n,CI m)
{
	return m?gcd(m,n%m):n;
}
int main()
{
	freopen("magic.in","r",stdin); freopen("magic.out","w",stdout);
	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&s[i].x,&s[i].y);
	for (sort(s+1,s+n+1),i=1;i<=200;++i) for (j=1;j<=200;++j)
	f[i][j]=f[i-1][j]+(g[i][j]=g[i][j-1]+(gcd(i,j)==1));
	for (memset(r,-1,sizeof(r)),memset(d,-1,sizeof(d)),i=n;i;--i)
	{
		int x=s[i].x,y=s[i].y; d[x][y]=d[x+1][y]+1; r[x][y]=r[x][y+1]+1; cur=ret; ret+=f[d[x][y]][r[x][y]];
		for (j=y+1;j<=y+r[x][y];++j) ans+=1LL*f[d[x][y]][j-y-1]*f[d[x][j]][r[x][j]],cur-=f[d[x][j]][r[x][j]];
		for (j=x+1;j<=x+d[x][y];++j) cur-=tag[j][y],ans+=1LL*g[j-x][r[x][y]]*cur;
		for (j=y;j<=y+r[x][y];++j) tag[x][j]+=f[d[x][y]][r[x][y]]-(j!=y?f[d[x][y]][j-y-1]:0);
	}
	return printf("%lld",ans<<1),0;
}

Postscript

STO CXR ORZ

原文地址:https://www.cnblogs.com/cjjsb/p/13714980.html