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
總結
以上是生活随笔為你收集整理的[20180816]校内模拟赛的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。