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<=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!
原文地址:https://www.cnblogs.com/PaperCloud/p/9495902.html