2019.08.30 日常总结

光阴如梭,离开学仅有1天!!!

一本通1562&LOJ10140:

【题意】:

Linux 用户和 OSX 用户一定对软件包管理器不会陌生。通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置。Debian/Ubuntu 使用的 apt-get,Fedora/CentOS 使用的 yum,以及 OSX 下可用的 Homebrew 都是优秀的软件包管理器。

 

你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包 AA 依赖软件包 BB,那么安装软件包 AA 以前,必须先安装软件包 BB。同时,如果想要卸载软件包 BB,则必须卸载软件包 AA。现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除 00 号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而 00 号软件包不依赖任何一个软件包。依赖关系不存在环(若有m(m≥2)个软件包A1,A2,A3,…,Am ,其中 A1 依赖 A2,A2依赖 A3 ,A3依赖 A4 ,……,Am−1 依赖 Am ,而 Am 依赖 A1 ,则称这 m 个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己。

现在你要为你的软件包管理器写一个依赖解决程序。根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为 0。

【思路】:首先,根据题意,这依赖关系形成了一棵树,所以我们首先进行树链剖分

接下来让我们考虑如何用线段树来维护信息:

记Tag[o]为线段树上编号为o的点的状态(-1表示不知道,0表示未安装,1表示以安装),一开始肯定全部清0,sum[o]表示线段树上编号为o的点的子树v中Tag[v]的点的个数

所以,接下来的维护很简单了,具体看代码。

【代码】:

#include <bits/stdc++.h>
using namespace std;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
const int N=1e5+3e2;
struct node{
	int next,to;
}e[N];int h[N],tot;
inline void add(int a,int b){
	e[++tot]=(node){h[a],b};h[a]=tot;
}
int dep[N],sze[N],son[N];
int fa[N],seg[N],rev[N];
int top[N],dfscnt;
void dfs1(int u,int f){
	dep[u]=dep[f]+1;sze[u]=1;fa[u]=f;
	int maxdep=-1,heavyson=0;
	for(int i=h[u];i;i=e[i].next){
		register int v=e[i].to;
		if (v==f) continue;dfs1(v,u);
		if (sze[v]>maxdep){
			maxdep=sze[v];
			heavyson=v;
		}
		sze[u]+=sze[v];
	}
	son[u]=heavyson;
}
void dfs2(int u,int Top){
	if (son[u]){
		seg[son[u]]=++dfscnt;
		rev[dfscnt]=son[u];
		top[son[u]]=Top;
		dfs2(son[u],Top);
	}
	else return;
	for(int i=h[u];i;i=e[i].next){
		register int v=e[i].to;
		if (v!=fa[u]&&v!=son[u]){
			seg[v]=++dfscnt;
			rev[dfscnt]=v;
			top[v]=v;dfs2(v,v);
		}
	}
	return;
}
int Tag[N<<2],sum[N<<2];
inline void pushup(int o){
	sum[o]=sum[o<<1]+sum[o<<1|1];
}
inline void pushdown(int o,int l,int r){
	Tag[o<<1]=Tag[o];Tag[o<<1|1]=Tag[o];
	register int mid=(l+r)>>1;
	sum[o<<1]=Tag[o]*(mid-l+1);
	sum[o<<1|1]=Tag[o]*(r-mid);
	Tag[o]=-1;return;
}
void build(int o,int l,int r){
	if (l==r){
		sum[o]=0;return;
	}
	int mid=(l+r)>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushup(o);return;
}
void update(int o,int l,int r,int p,int q,int v){
	if (p<=l&&r<=q){
		sum[o]=v*(r-l+1);
		Tag[o]=v;return;
	}
	if (l>q||r<p) return;
	if (Tag[o]!=-1) pushdown(o,l,r);
	register int mid=(l+r)>>1;
	update(o<<1,l,mid,p,q,v);
	update(o<<1|1,mid+1,r,p,q,v);
	pushup(o);return;
}
int lst,i,n,m,x;
char opt[20];
inline void change(int x,int y){
	while (top[x]!=top[y]){
		if (dep[top[x]]<dep[top[y]]) swap(x,y);
		update(1,1,n,seg[top[x]],seg[x],1);
		x=fa[top[x]];
	}
	if (dep[x]>dep[y]) swap(x,y);
	update(1,1,n,seg[x],seg[y],1);
}
int main(){
	freopen("t1.in","r",stdin);
	n=read();
	for(i=2;i<=n;i++){
		x=read()+1;add(x,i);
	}
	rev[1]=dfscnt=1;
	seg[1]=top[1]=1;//别忘了这两行哦
	dfs1(1,0);dfs2(1,1);
	build(1,1,n);m=read();
	for(i=1;i<=m;i++){
		scanf("%s",opt+1);
		x=read()+1;lst=sum[1];
		switch(opt[1]){
			case 'i':change(1,x);break;
			case 'u':update(1,1,n,seg[x],seg[x]+sze[x]-1,0);break;
//			seg[x]+sze[x]-1表示以x为根的子树的节点的seg值的最大值
		}
		printf("%d\n",abs(sum[1]-lst));//别忘了abs
	}
	return 0;
}

一本通1571&LOJ10149:

【题意】:给定一个具有N个顶点的凸多边形,将顶点从 1至 N标号,每个顶点的权值都是一个正整数。将这个凸多边形划分成 (N-2)个互不相交的三角形,试求这些三角形顶点的权值乘积和至少为多少。

【思路】:记f[i][j]表示把顶点i到j分为很多个三角形的最小权值和,则f[i][j]=min \{ f[i][k]+f[k][j]+s[i] \times s[j] \times s[k] \}(i < k < j),其中s[i]表示顶点i的权值

最后,别忘了打高精哦(个人推荐把高精封装到一个结构体里,这样使用非常方便)

【代码】:

//By hpwwzyy2012
#include <bits/stdc++.h>
using namespace std;
const int N=1100;
struct node{
	int a[N],len;
	node(){
		memset(a,0,sizeof(a));
		len=0;
	}//清空高精数
	node operator = (int c){
		if (c==0) len=1;
		while (c){
			a[++len]=c%10;
			c/=10;
		}
		return *this;
	}//赋值高精数(把高精数赋值一个低精数)
	node operator = (string s){
		len=s.length();
		for(int i=0;i<len;i++)
		a[len-i]=s[i]-'0';
		return *this;
	}//用string类型给高精赋值(可以这么用:a="154352345",a表示一个高精数)
	node operator = (node b){
		memcpy(a,b.a,sizeof(a));
		len=b.len;return *this;
	}//用高精给高精赋值
	node operator * (node b){
		node c;c.len=len+b.len+10;
		for(int i=1;i<=len;i++){
			register int x=0,j;
			for(j=1;j<=b.len;j++){
				c.a[i+j-1]+=a[i]*b.a[j]+x;
				x=c.a[i+j-1]/10;c.a[i+j-1]%=10;
			}
			if (x) c.a[i+b.len]=x;
		}
		while (c.len>1&&c.a[c.len]==0) c.len--;
		return c;
	}//高精乘
	node operator + (node b){
		node c;c.len=max(len,b.len);
		register int x=0,i;
		for(i=1;i<=c.len;i++){
			c.a[i]=a[i]+b.a[i]+x;
			x=c.a[i]/10;c.a[i]%=10;
		}
		if (x) c.a[++c.len]=x;
		return c;
	}//高精加
	bool operator < (node b){
		if (len<b.len) return true;
		if (len>b.len) return false;
		for(int i=len;i;i--){
			if (a[i]<b.a[i]) return true;
			if (a[i]>b.a[i]) return false;
		}
		return false;
	}//高精比较
	void read(){
		string s;
		cin>>s;
		*this=s;
	}//输入
	void write(){
//		printf("len=%d\n",len);
		for(int i=len;i;i--)
		printf("%d",a[i]);
//		printf("\n");
	}//输出
}f[60][60],s[60];int n;
inline void read_the_date(){
	cin>>n;int i;
	for(i=1;i<=n;i++)
	s[i].read();
	return;
}//读入数据
inline node Min(node a,node b){
	if (a<b) return a;
	else return b;
}//类min函数
inline void get_answer(){
	for(int i=1;i<=n;i++)
	for(int j=i;j<=n;j++){
		if (j-i<2) f[i][j]=0;
		else f[i][j].len=N-1;
	}//初始化
	for(int l=3;l<=n;l++)
	for(int j=l;j<=n;j++){
		int i=j-l+1,k;
//		printf("(%d,%d):",i,j);
		for(k=i+1;k<j;k++)
		f[i][j]=Min(f[i][j],f[i][k]+f[k][j]+s[i]*s[j]*s[k]);//dp
//		printf("(%d,%d)\n",i,j);
	}
}
inline int hpwwzyy2012(){
	read_the_date();
//	printf("read is OK!\n");
//	for(int i=1;i<=n;i++)
//	s[i].write();
	get_answer();
	f[1][n].write();
	return 0;
}
int main(){
	freopen("t1.in","r",stdin);
	return hpwwzyy2012();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值