2020牛客多校训练营(第七、八场)

第七场

H.Dividing

标签:整除分块

发现符合要求的(n,k)无非两种,要么n是k的倍数,要么n-1是k的倍数(n=1也算)

于是就把问题转化成求解 ∑ k = 1 n ⌊ N k ⌋ + ∑ k = 2 n ( ⌊ N − 1 k ⌋ + 1 ) \sum_{k=1}^n\lfloor\frac{N}{k} \rfloor+\sum_{k=2}^n(\lfloor\frac{N-1}{k} \rfloor+1) k=1nkN+k=2n(kN1+1)

可以直接整除分块 O ( n ) O(\sqrt{n}) O(n )求解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+5,MOD=1e9+7;

ll T,n,k,m;

ll sum,up,down,last;

int main()
{
	cin>>n>>k;
	if (k<=sqrt(n))
	{
		for (int i=1;i<=k;i++)
		{
			sum=(sum+n/i)%MOD;
			sum=(sum+(n-1)/i+1)%MOD;
		}
		sum=((sum-n)%MOD+MOD)%MOD;
	}
	else
	{
		up=sqrt(n);
		for (int i=1;i<=up;i++)
			sum=(sum+n/i)%MOD;
		down=n/k;
		last=k;
		for (int i=down;i<=up;i++)
		{
			sum=(sum+1ll*i*(last-n/(i+1)))%MOD;
			last=n/(i+1);
		}
		if (1ll*up*(up+1)>n) sum-=up;

		n--;
		up=sqrt(n);
		for (int i=1;i<=up;i++)
			sum=(sum+n/i)%MOD;
		down=n/k;
		last=k;
		for (int i=down;i<=up;i++)
		{
			sum=(sum+1ll*i*(last-n/(i+1)))%MOD;
			last=n/(i+1);
		}
		if (1ll*up*(up+1)>n) sum-=up;
		sum=((sum-n)%MOD+MOD)%MOD;
		sum=(sum+k-1)%MOD;
	}
	printf("%lld\n",sum);
	return 0;
}

B.Mask-Allocation

标签:思维,构造,gcd

题意真的很难看懂

构造方法类似于在n*m的矩阵里填正方形
在这里插入图片描述
比如这个7*4的,需要7组4个就横着看,需要4组7个就把每个正方形旋转90度之后竖着看。直觉上感觉这种构造方法比较优,但是正确性证不来。

写法就是类似于求gcd。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+5,INF=0x3f3f3f3f;

int T,n,m;

vector<int>ans;

int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%d%d",&n,&m);
		if (n<m) swap(n,m);
		ans.clear();
		while(n%m!=0)
		{
			for (int i=1;i<=n/m*m;i++) ans.push_back(m);
			int t=m;
			m=n%m;
			n=t;
		}
		for (int i=1;i<=n;i++) ans.push_back(m);
		printf("%d\n",ans.size());
		for (auto x:ans) printf("%d ",x);
		printf("\n");
	}
	return 0;
}

C.A-National-Pandemic

标签:树链剖分

树剖的就是一种能实现在 O ( l o g 2 n ) O(log^2n) O(log2n)的复杂度内修改和查询树上两点路径和的结构。
入门推荐
模板题
其复杂度一方面由线段树logn区间修改查询,另一方面由“任意结点到根最多经过logn条轻边”来保证。

怎么通过查询路径和解决这道题呢?

首先对于操作一,我们希望把这个操作从任意结点转移到根结点上,可以先对x到根每条边权值+2(在查询时作为补充),再修改全局变量global+=w-dep[x],把操作转化为对根节点的操作。

对于操作二,先查询(操作三)点值,然后对于大于0的部分,单独搞一个变量fix把大于0的部分抵消掉。

对于操作三查询直接给出计算方法
ans=qQuery(x,1)+global-tot*(dep[x])+fix[x];
也就是"x到根节点的路径和"+“全局变量”-“操作一的次数”*“x的深度”+“操作二的抵消部分”

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=5e4+5;

ll q[N];

int n,m,r,p,fix[N],tot,T,global;

struct segTree{
	ll t[N<<4];
	ll lztag[N<<4];
	void init()
	{
		memset(t,0,sizeof(t));
		memset(lztag,0,sizeof(lztag));
	}
	void pushdown(int o,int l,int r)
	{
		if (lztag[o])
		{
			int mid=(l+r)>>1;
			t[o*2]+=lztag[o]*(mid-l+1);
			t[o*2+1]+=lztag[o]*(r-mid);
			lztag[o*2]+=lztag[o];
			lztag[o*2+1]+=lztag[o];
			lztag[o]=0;
		}
	}
	void pushup(int o,int l,int r)
	{
		t[o]=t[o*2]+t[o*2+1];
	}
	void build(int o,int l,int r)
	{
		if (l==r)
		{
			//t[o]=ord[l]%p;
			return;
		}
		int mid=(l+r)>>1;
		build(o*2,l,mid);
		build(o*2+1,mid+1,r);
		pushup(o,l,r);
	}
	void modify(int o,int l,int r,int tl,int tr,int v)
	{
		if (tl<=l && r<=tr)
		{
			t[o]+=1ll*v*(r-l+1);
			lztag[o]+=v;
			return;
		}
		int mid=(l+r)>>1;
		pushdown(o,l,r);
		if (tl<=mid) modify(o*2,l,mid,tl,tr,v);
		if (tr>mid) modify(o*2+1,mid+1,r,tl,tr,v);
		pushup(o,l,r);
	}
	ll query(int o,int l,int r,int tl,int tr)
	{
		if (tl<=l && tr>=r) return t[o];
		int mid=(l+r)>>1;
		pushdown(o,l,r);
		if (tr<=mid) return query(o*2,l,mid,tl,tr);
		if (tl>mid) return query(o*2+1,mid+1,r,tl,tr);
		return query(o*2,l,mid,tl,tr)+query(o*2+1,mid+1,r,tl,tr);
	}
}ST;

int sz[N],dep[N],son[N],fa[N],top[N],num[N],cnt;

vector<int>G[N];

void init()
{
	memset(sz,0,sizeof(sz));
	memset(fix,0,sizeof(fix));
	tot=0;
	cnt=0;
	fa[1]=-1;
	dep[1]=1;
	top[1]=1;
	global=0;
}

void dfs1(int x)
{
	int mxson=-1;
	son[x]=-1;
	for (auto to:G[x])
	{
		if (to==fa[x]) continue;
		dep[to]=dep[x]+1;
		fa[to]=x;
		dfs1(to);
		if (sz[to]>mxson) mxson=sz[to], son[x]=to;
		sz[x]+=sz[to];
	}
	sz[x]++;
}
void dfs2(int x,int tp)
{
	num[x]=++cnt;
	top[x]=tp;
	if (~son[x]) dfs2(son[x],tp);
	for (auto to:G[x])
	{
		if (to==fa[x] || to==son[x]) continue;
		dfs2(to,to);
	}
}
void qModify(int x,int y,int z)
{
	while(top[x]!=top[y])
	{
		if (dep[top[x]]<dep[top[y]]) swap(x,y);
		ST.modify(1,1,n,num[top[x]],num[x],z);
		x=fa[top[x]];
	}
	if (dep[x]>dep[y]) swap(x,y);
	ST.modify(1,1,n,num[x],num[y],z);
}
void subrootModify(int x,int z)
{
	ST.modify(1,1,n,num[x],num[x]+sz[x]-1,z);
}

ll qQuery(int x,int y)
{
	ll res=0;
	while(top[x]!=top[y])
	{
		if (dep[top[x]]<dep[top[y]]) swap(x,y);
		res+=ST.query(1,1,n,num[top[x]],num[x]);
		x=fa[top[x]];
	}
	if (dep[x]>dep[y]) swap(x,y);
	res+=ST.query(1,1,n,num[x],num[y]);
	return res;
}

ll subrootQuery(int x)
{
	return ST.query(1,1,n,num[x],num[x]+sz[x]-1);
}

void solve()
{
	cin>>n>>m;
	for (int i=1;i<=n;i++) G[i].clear();
	for (int i=1;i<=n-1;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	init();
	dfs1(1);
	dfs2(1,1);
	ST.init();
	for (int i=1;i<=m;i++)
	{
		int opt,x,w;
		scanf("%d",&opt);
		if (opt==1)
		{
			scanf("%d%d",&x,&w);
			qModify(x,1,2);
			global+=w-dep[x];
			tot++;
		}
		if (opt==2)
		{
			scanf("%d",&x);
			int ask=qQuery(x,1)+global-tot*(dep[x])+fix[x];
			if (ask>0) fix[x]-=ask;
		}
		if (opt==3)
		{
			scanf("%d",&x);
			int ask=qQuery(x,1)+global-tot*(dep[x])+fix[x];
			printf("%d\n",ask);
		}
	}
}

int main()
{
	cin>>T;
	while(T--) solve();
	return 0;
}

第八场

K.Kabaleo-Lite

难点在于题意读不清楚和爆long long,以及怎么用unsigned long long处理负数。

一开始一直搞不清是优先“人数”还是优先“利润”,后来看了回答才搞清楚是优先人数,然而已经WA飞了。。

然后这道题最大值会到 1 e 19 1e19 1e19,只能用unsigned long long存,问题是怎么处理负数情况呢。可以开两个ull,遇到正数记在第一个变量里,遇到负数,把它取反之后记录在第二个变量里,然后判断这两个变量的大小来决定答案是否有负号。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+5;
const ll INF=0x3f3f3f3f3f3f3f3f;
int T,n;

ll A[N],B[N];
struct status{
	ll a,b;
};
vector<status>Q;
void solve(int caseNum)
{
	scanf("%d",&n);
	A[0]=0;
	for (int i=1;i<=n;i++)
	{
		scanf("%llu",&A[i]);
		A[i]+=A[i-1];
	}
	B[0]=INF;
	for (int i=1;i<=n;i++)
	{
		scanf("%llu",&B[i]);
		B[i]=min(B[i-1],B[i]);
	}
	Q.clear();
	for (int i=n;i>=1;i--)
	{
		while(!Q.empty() && Q.back().a<=A[i]) Q.pop_back();
		Q.push_back({A[i],B[i]});
	}
	ll sum=0;
	unsigned long long ans1=0,ans2=0;
	unsigned long long aa=0;
	for (auto now:Q)
	{
		if (now.a>=0)
			ans1+=1ll*now.a*(now.b-sum);
		else
			ans2+=1ll*(-now.a)*(now.b-sum);
		sum=now.b;
	}
	bool sig=0;
	unsigned long long ans;
	if (ans1>=ans2) ans=ans1-ans2;
	else
	{
		sig=1;
		ans=ans2-ans1;
	}
	printf("Case #%d: %lld ",caseNum,sum);
	if (sig) printf("-");
	printf("%llu\n",ans);
}
int main()
{
	cin>>T;
	for (int i=1;i<=T;i++) solve(i);
	return 0;
}

G.Game-SET

不知道怎么评价的一题。结论是只需要考虑前21个就可以了,那直接暴力肯定没问题。想不通为啥,也懒得证了。

然而直接写 O ( T ∗ n 3 ) O(T*n^3) O(Tn3)的暴力,找到解就return也一样能过。。

也不深究了,倒是对这种字符串的读入和处理方法可以学一下。

#include<bits/stdc++.h>
using namespace std;
 
const int N=300;
 
int A[N][4];
 
int T,n;
 
char s[100],o[100],p[100],q[100],r[100];
 
void Read(int x)
{
    scanf("%s",s);
    int len=strlen(s);
    for (int i=0;i<len;i++) if (s[i]=='[' || s[i]==']') s[i]=' ';
    sscanf(s,"%s%s%s%s",o,p,q,r);
    switch(o[1]){
        case 'n':A[x][0]=1; break;
        case 'w':A[x][0]=2; break;
        case 'h':A[x][0]=3; break;
        default:A[x][0]=0; break;
    }
    switch(p[0]){
        case 'd':A[x][1]=1; break;
        case 's':A[x][1]=2; break;
        case 'o':A[x][1]=3; break;
        default:A[x][1]=0; break;
    }
    switch(q[1]){
        case 'o':A[x][2]=1; break;
        case 't':A[x][2]=2; break;
        case 'p':A[x][2]=3; break;
        default:A[x][2]=0; break;
    }
    switch(r[0]){
        case 'r':A[x][3]=1; break;
        case 'g':A[x][3]=2; break;
        case 'p':A[x][3]=3; break;
        default:A[x][3]=0; break;
    }
}
 
bool judge(int a,int b,int c)
{
    for (int i=0;i<=3;i++)
    {
        if (A[a][i]==0 || A[b][i]==0 || A[c][i]==0) continue;
        int sum=A[a][i]+A[b][i]+A[c][i];
        if (sum!=6 && sum!=3 && sum!=9) return 0;
    }
    return 1;
}
void solve(int cs)
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) Read(i);
    for (int i=1;i<=min(21,n);i++)
        for (int j=i+1;j<=min(21,n);j++)
            for (int k=j+1;k<=min(21,n);k++)
            {
                if (judge(i,j,k))
                {
                    printf("Case #%d: %d %d %d\n",cs,i,j,k);
                    return;
                }
            }
    printf("Case #%d: -1\n",cs);
}
int main()
{
    cin>>T;
    for (int cs=1;cs<=T;cs++) solve(cs);
    return 0;
}
在使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值