更好的阅读 第一次进入时加载缓慢,请耐心等待。
赛时降智,菜是原罪。
A. Likes
简单题。
#include <bits/stdc++.h>
using namespace std;
int T,n,a[11111],s[11111];
int main(){
cin>>T;
while(T--){
cin>>n;
for(int i=1; i<=n; i++)cin>>a[i];
sort(a+1,a+n+1,[](int x,int y){return x>y;});
int l=1,r=n+1;
for(int i=1; i<=n; i++){s[i]=s[i-1]+(a[i]>0?1:-1);if(a[i]<0&&n+1==r)r=i;cout<<s[i]<<" ";}
puts("");
int now=0,nr=r;
while(l<nr){
a[++now]=1;
l++;
if(r<=n)a[++now]=-1,r++;
}
for(int i=1; i<=n; i++){s[i]=s[i-1]+a[i];cout<<s[i]<<" ";}
puts("");
}
return 0;
}
B. Settlement of Guinea Pigs
简单题,形如 111111111...00000
或 101010101010..0
。
#include <bits/stdc++.h>
using namespace std;
int T;
int n;
int a[111111];
void solve(){
cin>>n;
for(int i=1; i<=n; i++)cin>>a[i];
a[n+1]=2;
int Not=0,Ans=0,total=0;
for(int i=1; i<=n+1; i++){
if(a[i]==1){Not++;}
else {
Ans=max(Ans,Not+(total?total/2+1:0));
total+=Not;
Not=0;
}
}
cout<<Ans<<endl;
}
int main(){
cin>>T;
while(T--)solve();
return 0;
}
C. The Very Beautiful Blanket
有 \(a_{1,1}\oplus a_{1,2}\oplus a_{2,1}\oplus a_{2,2}=a_{3,3}\oplus a_{3,4}\oplus a_{4,3}\oplus a_{4,4}\) 和 \(a_{1,3}\oplus a_{1,4}\oplus a_{2,3}\oplus a_{2,4}=a_{3,1}\oplus a_{3,2}\oplus a_{4,1}\oplus a_{4,2}\)。
即 \(a_{1,1}\oplus a_{1,2}\oplus a_{2,1}\oplus a_{2,2}\oplus a_{1,3}\oplus a_{1,4}\oplus a_{2,3}\oplus a_{2,4}=a_{3,3}\oplus a_{3,4}\oplus a_{4,3}\oplus a_{4,4}\oplus a_{3,1}\oplus a_{3,2}\oplus a_{4,1}\oplus a_{4,2}\)。
和 \(a_{1,1}\oplus a_{1,2}\oplus a_{2,1}\oplus a_{2,2}\oplus a_{3,1}\oplus a_{3,2}\oplus a_{4,1}\oplus a_{4,2}=a_{3,3}\oplus a_{3,4}\oplus a_{4,3}\oplus a_{4,4}\oplus a_{1,3}\oplus a_{1,4}\oplus a_{2,3}\oplus a_{2,4}\)。
#include <bits/stdc++.h>
using namespace std;
void solve(){
int n,m;
cin>>n>>m;
cout<<n*m<<endl;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
printf("%d ",(i<<8)|(j));
}
puts("");
}
}
int main(){
int T;
cin>>T;
while(T--)
solve();
return 0;
}
D. Buying gifts
枚举第一个朋友的最大值,对于第二个朋友可以买的礼物用 set
存储即可。
#include <bits/stdc++.h>
using namespace std;
struct node{int x,y;};
void solve(){
int n;
cin>>n;
multiset<int> Set;
vector<node> a(n);
for(node &i:a)scanf("%d %d",&i.x,&i.y),Set.insert(i.y);
sort(a.begin(),a.end(),[](node x,node y){return x.x>y.x;});
int now=-0x3f3f3f3f,ans=0x3f3f3f3f;
for(int i=0; i<n; i++){
Set.erase(Set.find(a[i].y));
int x=a[i].x,y=a[i].y;
if(now>=x)ans=min(ans,now-x);
else{
ans=min(ans,x-now);
auto it=Set.lower_bound(x);
if(it!=Set.end())ans=min(ans,(*it)-x);
if(it!=Set.begin())ans=min(ans,abs(x-(*--it)));
}
now=max(now,y);
}
cout<<ans<<endl;
}
int main(){
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
E. Music Festival
定义 mx[i]
为第 \(i\) 个专辑的歌曲的最大值,明显的若先听第 \(i\) 个专辑,那么所有 mx[j]<=mx[i]
的专辑 \(j\) 对答案是没有贡献的,所以我们按 mx[i]
排序,定义 \(f_i\) 为 前 \(i\) 个专辑的最大值,枚举第 \(i\) 个专辑到底听几个即可进行转移。
注意本题卡 cin/cout
! 注意专辑内的歌曲顺序不能打乱!
#include <bits/stdc++.h>
using namespace std;
int sx[200000+1],dp[200000+1],mx[200000+1];
vector<int> a[200000+1];
int Len[200000+1];
void solve(){
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++){
int k,mx=-0x3f3f3f3f;
scanf("%d",&k);
Len[i]=0;
a[i].resize(k+1);
for(int j=1; j<=k; j++){
int x;
scanf("%d",&x);
if(x>mx)a[i][++Len[i]]=x,mx=x;
}
a[i].resize(Len[i]+1);
sx[i]=i;
}
sort(sx+1,sx+n+1,[](int x,int y){return a[x][Len[x]]<a[y][Len[y]];});
for(int i=1; i<=n; i++)mx[i]=a[sx[i]][Len[sx[i]]];
dp[0]=0;
for(int i=1; i<=n; i++){
dp[i]=dp[i-1];
int pos=sx[i];
for(int j=1; j<=Len[pos]; j++){
int othpos=lower_bound(mx+1,mx+n+1,a[pos][j])-mx;
dp[i]=max(dp[i],dp[othpos-1]+Len[pos]-j+1);
}
}
printf("%d\n",dp[n]);
return ;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
solve();
}
return 0;
}
F. The way home
贪心的想,演唱会只会在 w[i]
最高的地方演出。
定义 dp[i][j]
表示 目前在城市 i
,所有经过的城市中 w[i]
最大的城市为 j
的最小演唱次数,其次是最大钱数,进行转移即可,考虑到 n
比较小,可以用 floyd
进行预处理。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=3001;
ll value[N],dist[N][N],sx[N];
pair<ll,ll> ans[N];
void solve(){
ll n,m,p;
scanf("%lld %lld %lld",&n,&m,&p);
for(ll i=1; i<=n; i++){
scanf("%lld",&value[i]);
sx[i]=i;
for(ll j=1; j<=n; j++)dist[i][j]=i==j?0:1e18;
}
if(n>2)sort(sx+2,sx+n,[](ll x,ll y){return value[x]<value[y];});
for(ll i=1; i<=m; i++){
ll u,v,val;
scanf("%lld %lld %lld",&u,&v,&val);
dist[u][v]=min(dist[u][v],val);
}
for(ll k=1; k<=n; k++)for(ll i=1; i<=n; i++)
for(ll j=1; j<=n; j++)dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
for(ll i=2; i<=n; i++)ans[i]=make_pair(1e17,-1e17);
ans[1]=make_pair(0,-p);
for(ll i=1; i<=n; i++)
for(ll j=1; j<=n; j++)
if(dist[sx[i]][j]!=1e18){
ll v=max(0ll,(dist[sx[i]][j]+ans[sx[i]].second+value[sx[i]]-1)/value[sx[i]]);
ans[j]=min(ans[j],make_pair(ans[sx[i]].first+v,ans[sx[i]].second-v*value[sx[i]]+dist[sx[i]][j]));
}
if(ans[n].first>1e16)puts("-1");
else cout<<ans[n].first<<endl;
return ;
}
int main(){
ll T;
scanf("%lld",&T);
while(T--){
solve();
}
return 0;
}
G. Gasoline prices
明显的,每一次操作是对两条路径的第 i
个节点进行取交(两个区间的交集)操作,考虑暴力对其进行取交操作。
不明显的,我们要优化暴力。
考虑并查集进行优化,减少无意义的计算,若 \(a\) 至 \(b\) 和 \(c\) 至 \(d\) 进行了取交操作, \(e\) 至 \(f\) 和 \(c\) 至 \(d\) 也进行了取交操作,那么再对 \(a\) 至 \(b\) 和 \(e\) 至 \(f\) 进行了取交操作是没有意义的。(\(a,b\) 的 \(lca\) 是 b, \(c,d,e,f\) 同理)
考虑并查集维护什么,可以发现,我们只用维护长度 \(2^i\) 的段(该段的端点 \(a,b\) 满足 \(a\) 是 \(b\) 的 祖先或满足 \(b\) 是 \(a\) 的祖先),方便计算,主要是空间开不下。
算法的主体已经确定:暴力修改加并查集剪掉无用计算,每一次修改的长度都是 \(2^i\) ,时间复杂度为 \(O(\text{能过})\) 。
对于暴力,一共有两种情况:
-
同向,若两个长度是 \(2^i\) 的段已经在同一个集合,直接返回,否则递归处理两个长度为 \(2^{i-1}\) 的子区间,顺便合并两个段所在的集合。
-
逆向,和同向相似,只是递归的子区间不同。
考虑并查集的套路,扩展域即可同时维护两种情况。
暴力合并两个点:
// 合并两个点
void merge(int x,int y){
x=find(x,f1),y=find(y,f1);
if(x==y)return;
ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[x]-L[x]+1)),mod-2)%mod;
ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[y]-L[y]+1)),mod-2)%mod; // 撤销贡献
f1[x]=y;
L[y]=max(L[x],L[y]);
R[y]=min(R[x],R[y]);
ans=1ll*ans*1ll*1ll*max(0ll,1ll*(R[y]-L[y]+1))%mod; // 加上新贡献
}
暴力合并两个长度为 \(2^i\) 的同向段:
// 合并同向等长段
void merge1(int x,int y,int ith){
if(find(x,f2[ith])==find(y,f2[ith]))return; // 剪枝
if(ith==0)return merge(x,y),void(); // 直接合并
f2[ith][find(x,f2[ith])]=find(y,f2[ith]); // 并查集合并
merge1(x,y,ith-1);
merge1(ST[x][ith-1],ST[y][ith-1],ith-1); // 暴力合并两个子段
}
暴力合并两个逆向段:
// 合并逆向等长段
void merge2(int x,int y,int ith){
if(find(x,f2[ith])==find(y+n,f2[ith]))return; // 剪枝
if(ith==0)return merge(x,y),void();
f2[ith][find(x+n,f2[ith])]=find(y,f2[ith]);// 并查集合并
f2[ith][find(x,f2[ith])]=find(y+n,f2[ith]);
merge2(x,ST[y][ith-1],ith-1); // 与合并同向等长段不同
merge2(ST[x][ith-1],y,ith-1); // 暴力合并两个子段
}
总代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+11,mod=1e9+7;
int ST[MAXN][18],depth[MAXN];
int h[MAXN],nt[MAXN<<1],to[MAXN<<1],linkcnt;
int L[MAXN],R[MAXN],n,ans=1;
int f1[MAXN],f2[18][MAXN<<1];
int ksm(int x,int y){
int cur=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)cur=1ll*cur*x%mod;
return cur;
}
// UNION_FIND_SETS
int find(int u,int *UNION_FIND_SETS){
return UNION_FIND_SETS[u]==u?u:UNION_FIND_SETS[u]=find(UNION_FIND_SETS[u],UNION_FIND_SETS);
}
void link(int u,int v){
nt[++linkcnt]=h[u],h[u]=linkcnt,to[linkcnt]=v;
}
void dfs_init(int u,int father){
depth[u]=depth[father]+1;
f1[u]=u;
for(int i=0; i<=17; i++)
f2[i][u]=u,f2[i][u+n]=u+n;
ST[u][0]=father;
for(int i=1; i<=17; i++)
ST[u][i]=ST[ST[u][i-1]][i-1];
for(int i=h[u]; i; i=nt[i]){
if(to[i]==father)continue;
dfs_init(to[i],u);
}
}
int LCA(int u,int v){
if(depth[u]<=depth[v])swap(u,v);
for(int i=17; i>=0; i--)if(depth[ST[u][i]]>=depth[v])u=ST[u][i];
if(u==v)return u;
for(int i=17; i>=0; i--)if(ST[u][i]!=ST[v][i])u=ST[u][i],v=ST[v][i];
return ST[u][0];
}
int kth_fa(int u,int dist){
for(int i=0; i<=17; i++)
if(((1<<i)&dist))u=ST[u][i];
return u;
}
// 合并两个点
void merge(int x,int y){
x=find(x,f1),y=find(y,f1);
if(x==y)return;
ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[x]-L[x]+1)),mod-2)%mod;
ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[y]-L[y]+1)),mod-2)%mod;
f1[x]=y;
L[y]=max(L[x],L[y]);
R[y]=min(R[x],R[y]);
ans=1ll*ans*1ll*1ll*max(0ll,1ll*(R[y]-L[y]+1))%mod;
}
// 合并同向等长段
void merge1(int x,int y,int ith){
if(find(x,f2[ith])==find(y,f2[ith]))return;
if(ith==0)return merge(x,y),void();
f2[ith][find(x,f2[ith])]=find(y,f2[ith]);
merge1(x,y,ith-1);
merge1(ST[x][ith-1],ST[y][ith-1],ith-1);
}
// 合并逆向等长段
void merge2(int x,int y,int ith){
if(find(x,f2[ith])==find(y+n,f2[ith]))return;
if(ith==0)return merge(x,y),void();
f2[ith][find(x+n,f2[ith])]=find(y,f2[ith]);
f2[ith][find(x,f2[ith])]=find(y+n,f2[ith]);
merge2(x,ST[y][ith-1],ith-1);
merge2(ST[x][ith-1],y,ith-1);
}
int main(){
scanf("%d",&n);
for(int i=2; i<=n; i++){
static int f;
scanf("%d",&f);
link(f,i);
}
dfs_init(1,0);
for(int i=1; i<=n; i++){
scanf("%d %d",&L[i],&R[i]);
ans=1ll*1ll*ans*max(0ll,1ll*(R[i]-L[i]+1))%mod;
}
int Q_sum;
scanf("%d",&Q_sum);
for(int Text=1; Text<=Q_sum; Text++){
int a,b,c,d,lca_of_a_b,lca_of_c_d,T;
scanf("%d %d %d %d",&a,&b,&c,&d);
lca_of_a_b=LCA(a,b);
lca_of_c_d=LCA(c,d);
// 合并同向等长段
T=min(depth[a]-depth[lca_of_a_b],depth[c]-depth[lca_of_c_d]);
for(int i=17; i>=0; i--)if((T&(1<<i)))merge1(a,c,i),a=ST[a][i],c=ST[c][i];
T=min(depth[b]-depth[lca_of_a_b],depth[d]-depth[lca_of_c_d]);
for(int i=17; i>=0; i--)if((T&(1<<i)))merge1(b,d,i),b=ST[b][i],d=ST[d][i];
// 合并逆向等长段
if(a==lca_of_a_b){
T=depth[b]-depth[lca_of_a_b]+1;
for(int i=17; i>=0; i--){
if((T&(1<<i))){
T^=(1<<i);
merge2(b,kth_fa(c,T),i);
b=ST[b][i];
}
}
}
else{
T=depth[a]-depth[lca_of_a_b]+1;
for(int i=17; i>=0; i--){
if((T&(1<<i))){
T^=(1<<i);
merge2(a,kth_fa(d,T),i);
a=ST[a][i];
}
}
}
printf("%d\n",ans);
}
return 0;
}
标签:857,return,ith,int,Codeforces,find,ans,Div,oplus
From: https://www.cnblogs.com/dadidididi/p/17206044.html