• hgoi#20191114


    T1-宇宙魔方

    有一个 (n^3) 的立方体,每次会给这个立方体的一层都加上 (X) 或整体转动这个立方体
    现在给你若干次操作后的立方体,有一个格子上的值不知道,请你求出

    解法

    直接模拟回溯所有的操作
    具体是每次找到一层的最小值
    全部减去即可

    ac代码

    #include<bits/stdc++.h>
    #define inf 0x3f3f3f3f
    using namespace std;
    inline void read(int&x)
    {
    	x=0;int fh=1;char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
    	for(;isdigit(c);c=getchar())x=x*10+c-'0';
    	x*=fh;
    }
    inline void print(int x)
    {
    	if(x>9)print(x/10);
    	putchar(x%10+'0');
    }
    int n,minn,a[110][110][110];
    int main()
    {
    	freopen("cube.in","r",stdin);
    	freopen("cube.out","w",stdout);
    	read(n);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				read(a[i][j][k]);
    	for(int i=1;i<=n;i++)
    	{
    		minn=inf;
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				if(a[i][j][k]>=0)
    					minn=min(minn,a[i][j][k]);
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				a[i][j][k]-=minn;
    		minn=inf;
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				if(a[j][i][k]>=0)
    					minn=min(minn,a[j][i][k]);
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				a[j][i][k]-=minn;
    		minn=inf;
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				if(a[j][k][i]>=0)
    					minn=min(minn,a[j][k][i]);
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				a[j][k][i]-=minn;
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				if(a[i][j][k]<0)
    					print(-a[i][j][k]-1);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    T2-战争

    给你一个带权无向图,要求你每次走的边权都要严格递增
    求最远能走的距离

    解法

    这个根本不用建图
    (f_{i,j}) 表示 (i) 号点作为终点,上一个走的权值为 (j) 的最远距离
    我们发现一条边只会更新两个点,所以把第二维压掉
    具体见代码

    ac代码

    #include<bits/stdc++.h>
    using namespace std;
    inline void read(int&x)
    {
    	x=0;int fh=1;char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
    	for(;isdigit(c);c=getchar())x=x*10+c-'0';
    	x*=fh;
    }
    inline void print(int x)
    {
    	if(x>9)print(x/10);
    	putchar(x%10+'0');
    }
    struct node
    {
    	int x,y,s;
    	void init(){read(x),read(y),read(s);}
    	bool operator<(const node&a)const{return s<a.s;}
    }e[50010];
    queue<int>q;
    int n,m,ans,f[50010],g[50010];
    int main()
    {
    	freopen("war.in","r",stdin);
    	freopen("war.out","w",stdout);
    	read(n),read(m);
    	for(int i=1;i<=m;i++)e[i].init();
    	sort(e+1,e+m+1);
    	int nw=1,ed=1;
    	while(nw<=m)
    	{
    		while(ed<m&&e[ed].s==e[ed+1].s)ed++;
    		while(nw<=ed)
    		{
    			g[e[nw].x]=max(g[e[nw].x],max(f[e[nw].x],f[e[nw].y]+1));
    			g[e[nw].y]=max(g[e[nw].y],max(f[e[nw].y],f[e[nw].x]+1));
    			q.push(e[nw].x),q.push(e[nw].y),nw++;
    		}ed++;
    		while(!q.empty())f[q.front()]=g[q.front()],q.pop();
    	}
    	for(int i=0;i<n;i++)ans=max(ans,f[i]);print(ans);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    T3-和平

    给你一棵带边权的树,若干次询问
    每次询问 (A、B、c) 表示从A出发,走到B,只能通过边权大于等于 (c) 的点
    求最多能走到哪里

    解法

    先拆成两部分,先从 (A) 走到 (LCA) ,再从 (LCA) 走到 (B)
    如果停下的地方在第一部分,用倍增非常好做
    但是如果停下的地方在第二部分怎么办呢
    我们可以二分求这条路径的断点,然后求出一段的最小值,看是否符合要求
    具体实现见代码

    ac代码

    #include<bits/stdc++.h>
    #define inf 0x3f3f3f3f
    #define mid (l+r>>1)
    #define pb push_back
    #define N 100010
    #define K 18
    using namespace std;
    inline void read(int&x)
    {
    	x=0;int fh=1;char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
    	for(;isdigit(c);c=getchar())x=x*10+c-'0';
    	x*=fh;
    }
    inline void print(int x)
    {
    	if(x>9)print(x/10);
    	putchar(x%10+'0');
    }
    vector<int>e[N],w[N];
    int n,m,x,y,z,d[N],f[N][K],v[N][K];
    void dfs(int u,int fa)
    {
    	int sz=e[u].size();
    	for(int i=0;i<sz;++i)if(e[u][i]!=fa)
    		f[e[u][i]][0]=u,v[e[u][i]][0]=w[u][i],
    		d[e[u][i]]=d[u]+1,dfs(e[u][i],u);
    }
    inline int lca(int x,int y)
    {
    	if(d[x]<d[y])swap(x,y);
    	if(d[x]>d[y])
    	{
    		int g=d[x]-d[y];
    		for(int i=0;i<K;++i)
    			if(g&(1<<i))
    				x=f[x][i];
    	}
    	if(x==y)return x;
    	for(int i=K-1;i>=0;--i)
    		if(f[x][i]!=f[y][i])
    			x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    inline int get(int u,int g)
    {
    	int ans=inf;
    	for(int i=0;i<K;++i)
    		if(g&(1<<i))
    			ans=min(ans,v[u][i]),
    			u=f[u][i];
    	return ans;
    }
    //得到u向上g条边的最小值
    inline int find(int u,int g)
    {
    	for(int i=0;i<K;++i)
    		if(g&(1<<i))
    			u=f[u][i];
    	return u;
    }
    //寻找u向上g条边的点
    inline int query(int st,int ed,int G)
    {
    	int LCA=lca(st,ed),da=d[st]-d[LCA],db=d[ed]-d[LCA];
    	int l=0,r=da+db,ans,D=get(st,da);
    	while(l<=r)
    		if((mid<=da?get(st,mid):min(D,get(find(ed,da+db-mid),mid-da)))<G)r=mid-1;else ans=mid,l=mid+1;
    	//二分这个断点,如果在第一部分,那么路径只有一段
    	//如果在第二部分,分成整个第一部分和第二部分断点上方的位置求解
    	if(ans>da)return find(ed,da+db-ans);else return find(st,ans);
    }
    int main()
    {
    	freopen("peace.in","r",stdin);
    	freopen("peace.out","w",stdout);
    	read(n),read(m);
    	for(int i=1;i<n;i++)
    		read(x),read(y),read(z),
    		e[x].pb(y),w[x].pb(z),
    		e[y].pb(x),w[y].pb(z);
    	dfs(1,0);
    	for(int j=1;j<K;++j)
    		for(int i=1;i<=n;++i)
    			f[i][j]=f[f[i][j-1]][j-1],
    			v[i][j]=min(v[i][j-1],v[f[i][j-1]][j-1]);
    	for(int i=1;i<=m;++i)
    		read(x),read(y),read(z),
    		print(query(x,y,z)),putchar('
    ');
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    章节十:Selenium
    章节七:csv&excel
    章节十三:协程实践
    章节十二:协程
    章节八:爬取知乎文章
    章节十五:Scrapy实操
    章节十六:复习与反爬虫
    章节十一:定时与邮件
    章节十四:Scrapy框架
    阅读习惯2
  • 原文地址:https://www.cnblogs.com/muronglin/p/hgoi-20191114.html
Copyright © 2020-2023  润新知