[20180816]校内模拟赛

T1 清理(clear)


问题描述

小 C 最近自己开发了一款云盘软件,目前已有𝑛个用户。小C 的云盘上的文件会被后台分成两种类型,活动

文件和非活动文件,活动文件即可能常用的文件,会被放在高速服务器上给用户提供高速下载服务。用户

上传一个文件时,这个文件会被设置为活动文件。由于高速服务器内存大小有限,小 C 需要把一些文件

设为非活动文件,有以下两种设置方式:1.把上传时间前𝑥早的文件全部设为非活动文件;2.把第𝑥个用户上

传的文件全部设为非活动文件。注意这两种方式操作的对象都是所有文件,也就是说非活动文件可以被重

复设为非活动文件。

现在小 C 需要你写一个程序来维护所有文件的类型,并在每次操作后输出当前活动文件的数量,假设一开

始没有任何文件。


输入格式

第一行两个正整数𝑛,𝑚,其中𝑚表示操作数。

接下来𝑚行,每行两个正整数𝑜𝑝𝑡,𝑥,若𝑜𝑝𝑡 = 1,表示第𝑥个用户上传了一个文件;若𝑜𝑝𝑡 = 2,表示将第𝑥个

用户上传的文件全部设为非活动文件;若𝑜𝑝𝑡 = 3,表示将上传时间前𝑥早的文件设为非活动文件,保证此时

𝑥不超过当前总文件数。


输出格式

输出𝑚行,表示每次操作结束后的活动文件数量。


样例

样例输入


 3 5 
 1 1 
 1 2 
 1 3 
 2 1 
 3 2 
 


样例输出


 1 
 2 
 3 
 2 
 1 
 


数据范围

对于 100%的数据,𝑛,𝑚 ≤ 3 ∗ 10^5。


Solution

维护每个用户的总文件数以及非活动文件数(非活动文件必然是该用户前k个上传的文件)记录第i个上传的文件是上传用户的第几个文件进行将前x个文件设为非活动的操作时,假设之前进行过的这个操作最大的x为max,只要处理[max+1,x]内的文件,更新用户的非活动文件数顺便计算答案即可,每个文件最多被处理一次,总时间复杂度O(m).

PS:然而,我却打了一个O(mlogm)的。


#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define MN 300005
#define lowbit(x) (x&-(x))
int n,m,opt,x,del,cnt;
int a[MN],sum,last[MN],total[MN];
std::vector<int> pos[MN];
int t[MN];
inline void C(int x){for(;x<=m;x+=lowbit(x)) t[x]++;}
inline int G(int x){int res=0;for(;x;x-=lowbit(x)) res+=t[x];return res;}
int main(){
	freopen("clear.in","r",stdin);
	freopen("clear.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=m;i++){
		opt=read(),x=read();
		if(opt==1){
			a[x]++;sum++;total[x]++;
			pos[x].push_back(++cnt);
		}
		else if(opt==2){
			for(int j=last[x]+1;j<=total[x];j++) C(pos[x][j-1]);
			last[x]=total[x];
			sum-=a[x];a[x]=0;
		}
		else if(opt==3){
			del=std::max(del,x);
		}
		printf("%d\n",sum-del+G(del));
	}
	return 0;
}




T2 分组(group)


问题描述

有𝑛个人,你要把他们分成若干组,第𝑖个人希望自己所在组内人数不少于𝑎𝑖,求最多分成多少组。


输入格式

第一行一个正整数𝑛。

接下来𝑛行,每行一个正整数,表示𝑎𝑖。


输出格式

输出一个整数,表示答案,数据保证有解。


样例


样例输入


5
2
1
2
2
3



样例输出


2



数据范围

对于 100%的数据,n≤ 10^6 。


Solution

考虑最大的ai所在的组,这个组的限制只有人数不少于这个最大的ai,其他成员并没有影响,故将尽量大的

ai放到这个组内显然最优如果这个组贪心的恰好取最大的ai个,容易构造出反例(3 3 3 3 1 1)将ai从小到大

排序,不难发现,一定存在一种最优方案满足每组是排序后的一个区间用f[i]表示排序后前i个人最多分成几

组,那么f[i]=max(f[j]+1) (j=0~i-ai-1)用前缀max可以优化到O(n)


#include<cstdio>
#include<cstring>
#include<algorithm>
#define MN 1000005
#define ll long long
#define inf (1e9)
#define lowbit(x) (x&(-x))
inline ll read(){
	ll x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
ll n,a[MN],res;
ll t[MN],f[MN];
inline void rw(ll &x,ll y){if(y>x) x=y;}
inline void C(int y,ll x){rw(t[y],x);for(;y<=n+1;y+=lowbit(y))rw(t[y],x);}
inline ll G(int x){res=-inf;for(;x;x-=lowbit(x)) rw(res,t[x]);return res;}
int main(){
	freopen("group.in","r",stdin);
	freopen("group.out","w",stdout);
	n=read();
	register int i;
	for(i=1;i<=n;i++) a[i]=read();
	std::sort(a+1,a+n+1);
	C(1,0);
	for(i=1;i<=n;i++){
		if(i+1<=a[i]) continue;
		else{
			f[i]=G(i+1-a[i])+1;
			C(i+1,f[i]);
		}
	}
	printf("%lld\n",f[n]);
	return 0;
}




T3 排序(sort)


问题描述

Bob 有一个𝑛个数的数组,下标为0~𝑛 − 1且数组里恰好包含了0~𝑛 − 1各一次。 Bob 打算拿这个数组去和

Alice 玩一个游戏,Alice 和 Bob 轮流操作𝑚轮,每轮两人各操作一次,Alice 先,操作时需要选择数组中的

两个元素(可以重复)交换,一旦数组变成有序的,Bob 就获得胜利且游戏结束,若𝑚轮结束后 Bob 都没

有获胜,Alice 获胜。

可惜 Alice 没空,并不想陪 Bob 玩,Alice 告诉 Bob,如果有第𝑖轮,他就选择下标为𝑥𝑖和𝑦𝑖的元素交换。

Bob 想知道,自己最少能用几轮获胜。


输入格式

第一行一个正整数𝑛。

第二行𝑛个非负整数,表示 Bob 的数组。

第三行一个正整数𝑚。

接下来𝑚行,每行两个非负整数𝑥𝑖,𝑦𝑖。


输出格式

输出一个整数,表示答案,如果 Bob 不能获胜,输出−1。


样例

样例输入


2
0 1
2
0 0
0 0



样例输出


0



数据范围

对于 100%的数据,n ≤ 2* 10^5 ,n≤m≤ 6*10^5 。


Solution

考虑依次进行两个交换操作swap(a,b);swap(c,d);(假设a<b,c<d)

如果abcd是四个不同的元素或a=c且b=d,显然变成swap(c,d);swap(a,b);结果不变

如果b=c或a=d,我们可以把操作看成swap(x,y);swap(y,z);,此时把操作改成swap(y,z);swap(x,z);结果不变

对于两个相邻操作,我们可以修改前一个操作再将这个操作放到后面,结果不变

本题中,操作序列可以看成ABABAB...,A代表Alice的操作,B代表Bob的操作,其中Bob的操作是可以我们

自己决定的,我们可以通过交换相邻操作并修改Bob的操作来让序列变为AAA...BBB...

由于我们可以把一次交换操作再交换回来,故如果第i轮能获胜,第i+1轮也能获胜

二分答案ans,模拟Alice的前ans轮操作,只要计算Bob至少需要几次操作才能完成排序即可判断答案合法性

让i向ai连边,对于每个大小为size的环,需要size-1次操作来完成排序

答案显然不超过n,总时间复杂度不超过O(nlogn)


#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define MN 200005
#define MM 600005
#define mid (l+r>>1)
int a[MN],n,m,optx[MM],opty[MM];
int change[MN];
bool vis[MN];
int getsize(int x){
	int size=1;vis[x]=1;
	for(int i=change[x];i!=x;i=change[i]) vis[i]=1,size++;
	return size;
}
int getstep(){
	int res=0;
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;i++){
		if(!vis[i]) res+=getsize(i)-1;
	}
	return res;
}
bool check(int x){
	register int i;
	for(i=1;i<=n;i++) change[i]=a[i];
	for(i=1;i<=x;i++) std::swap(change[optx[i]],change[opty[i]]);
	if(getstep()<=x) return true;
	else return false;
}
int main(){
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	register int i;
	n=read();
	for(i=1;i<=n;i++) a[i]=read()+1;
	m=read();
	for(i=1;i<=m;i++) optx[i]=read()+1,opty[i]=read()+1;
	int l=0,r=m,ans;
	while(l<=r){
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	if(l>m) return 0*puts("-1");
	else return 0*printf("%d\n",ans);
}




T4 附加题


这是题面

Solution

先考虑 \(M=0\) 的情况,即对每个 \(i\) 求到其他所有点的距离之和

先考虑一个暴力的做法,用 \(f[i][x]\) 表示以 \(i\) 为根的子树 \(x\) 内所有点到 \(x\) 的距离之和,枚举一个儿子 \(y\) 转移,

\(f[i][x]+=f[i][y]+size[y]*w\)

对每个i做一遍DP,时间复杂度为 \(O(n^2)\) 考虑两个相邻的点 \(i\)\(j\) ,分别以 \(i\) 为根和以$ j$ 为根,不同的子树只有子

\(i\) 和子树 \(j\) .假设已经求出了 \(f[i][x]\) ,我们只需要重新计算 \(f[j][i]\)\(f[j][j]\) 就能求出所有 \(f[j][x]\)

具体地说,我们让 \(f[i][i]\) 减去从 \(j\) 转移来的答案,得到 \(f[j][i]\) ,再用 \(f[j][i]\) 转移到 \(f[i][j]\) 得到 \(f[j][j]\)

我们可以先求出所有 \(f[1][x]\) ,接着dfs一遍整棵树,维护以当前dfs到的点为根的DP值,即可 \(O(n)\) 求出所有答

案对于 \(M\le 15\) 的情况,距离异或上 \(M\) 只有二进制下后 \(4\) 位有影响,我们DP状态中再加一维,\(f[i][x][k]\) 表示

以i为根的子树x内到x距离模 \(16\)\(k\) 的点的距离之和,后面的换根操作同理,总时间复杂度 \(O(nM)\)


#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define MM 15
#define MN 100005
struct edge{int to,w,nex;}e[MN<<1];int cnt,hr[MN];
inline int ins(int f,int t,int w){
	e[++cnt]=(edge){t,w,hr[f]};hr[f]=cnt;
	e[++cnt]=(edge){f,w,hr[t]};hr[t]=cnt;
}
int n,M;
int f[MN][MM+5],g[MN][MM+5],ans[MN];
inline void dp(int fa,int x){
	g[x][0]=1;register int i,j;
	for(i=hr[x];i;i=e[i].nex)if(e[i].to^fa){
		dp(x,e[i].to);
		for(j=0;j<=MM;j++){
			f[x][(j+e[i].w)&MM]+=f[e[i].to][j]+g[e[i].to][j]*e[i].w; 
			g[x][(j+e[i].w)&MM]+=g[e[i].to][j];
		}
	}
}
inline void dfs(int fa,int x){
	register int i,j;
	for(i=0;i<=MM;i++) ans[x]+=f[x][i]-g[x][i]*i+g[x][i]*(i^M);
	for(i=hr[x];i;i=e[i].nex)if(e[i].to^fa){
		for(j=0;j<=MM;j++){
			f[x][(j+e[i].w)&MM]-=f[e[i].to][j]+g[e[i].to][j]*e[i].w;
			g[x][(j+e[i].w)&MM]-=g[e[i].to][j];
		}
		for(j=0;j<=MM;j++){
			f[e[i].to][(j+e[i].w)&MM]+=f[x][j]+g[x][j]*e[i].w;
			g[e[i].to][(j+e[i].w)&MM]+=g[x][j];
		}
		dfs(x,e[i].to);
		for(j=0;j<=MM;j++){
			f[e[i].to][(j+e[i].w)&MM]-=f[x][j]+g[x][j]*e[i].w;
			g[e[i].to][(j+e[i].w)&MM]-=g[x][j];
		}
		for(j=0;j<=MM;j++){
			f[x][(j+e[i].w)&MM]+=f[e[i].to][j]+g[e[i].to][j]*e[i].w;
			g[x][(j+e[i].w)&MM]+=g[e[i].to][j];

		}
	}
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();M=read();register int ai,bi,ci,i;
	for(i=1;i<n;i++){
		ai=read(),bi=read(),ci=read();
		ins(ai,bi,ci);
	}
	dp(0,1); dfs(0,1);
	for(i=1;i<=n;i++) printf("%d\n",ans[i]-M);
}





Blog来自PaperCloud,未经允许,请勿转载,TKS!

posted @ 2018-08-17 23:29  PaperCloud  阅读(350)  评论(0编辑  收藏  举报