2025/3/31

赛时

T1 很唐,哈希水题
搞搞就过了
期望得分 \(\text{100}\)
8:16

后面的题看了一遍
都很难受
要不先干 T2?

这山本数据实在是过于离谱了
我感觉判个 \(\text{Impossible!}\) 都能得很多
所以说其实我们可以先搞一个归并逆序对
然后合法再想办法
\(n^2\) 暴力的话大概也就只有 \(\text{20 pts}\)
思考正解
数据 \(n≤10^6,k≤10^{12}\)
那我们大概需要一个 \(n\log n\) 的复杂度
仔细读题面发现一个细节
a 数组互不相同
那能不能搞一下呢
算了不管了先打个暴力再说

#include<bits/stdc++.h>
using namespace std;
const int N=100098;
typedef long long ll;
ll k,cnt=0;
int n,a[N],b[N],ltmp[N],rtmp[N];
void MSORT(int l,int r){
	if(l>=r)return;
	int mid=(l+r)>>1;
	MSORT(l,mid),MSORT(mid+1,r);
	for(int i=l;i<=mid;i++)ltmp[i]=b[i];
	for(int i=mid+1;i<=r;i++)rtmp[i]=b[i];
	int ml=l,mr=mid+1,idx=l-1;
	while(ml<=mid&&mr<=r){
		if(ltmp[ml]<rtmp[mr])b[++idx]=ltmp[ml++];
		else b[++idx]=rtmp[mr++],cnt+=mid-ml+1;
	}
	while(ml<=mid)b[++idx]=ltmp[ml++];
	while(mr<=r)b[++idx]=rtmp[mr++];
}
int main(){
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
	MSORT(1,n);
	if(k>cnt)puts("Impossible!"),exit(0);
	else if(k==cnt){
		for(int i=1;i<=n;i++)printf("%d ",a[i]);
		exit(0);
	}
	for(int i=1;i<n;i++) for(int j=1;j<=n-i;j++){
		if(a[j]>a[j+1]){
			swap(a[j],a[j+1]),--k;
			if(k==0){
				for(int i=1;i<=n;i++)printf("%d ",a[i]);
				exit(0);
			}
		}
	}
	return 0;
}

期望得分 \(\text{120}\)
9:31

决定打一个 T3 暴力代码
这题看起来很离谱
\(\max(a_x+a_y,b_x+b_y)\)
我们分析一下
首先这题有两个操作:插入和删除
我们首先要解决的一个问题就是:怎么查找?
我觉得这个其实开一个 \(\text{unordered\_map}\) 哈希一下就可以了
比如说 \(a*1e9+b\)
这样就可以实现查找删除 \(O(1)\)
但下一个问题就是题里说的:咋找这个值呢?
暴力的话我们可能要枚举两个集合中所有元素
能过 \(20\) 就很不错了
如果我们开一个 \(\text{Map}\)
能快点吗?
我们可以搞一个当前最优答案
整一个类似最优性剪枝的抽象东西
基本它是按照 \(A\) 自动排好序的
加上 4s

今日我一个 \(map\) 夺得二十大点,不是问题

加一个小小的优化
就是我们记录上一次的答案
如果是插入的话
我们直接卡住这个答案枚举即可

#include<bits/stdc++.h>
#ifdef ONLINE_JUDGE
#define getchar getchar_unlocked
#define putchar putchar_unlocked
#endif
using namespace std;
typedef long long ll;
map<ll,int> s[2];
const ll base=1e9+1;
inline ll gh(ll a,ll b){return a*base+b;}
inline void rh(ll tmp,ll &a,ll &b){a=tmp/base,b=tmp%base;}
ll worst;
inline int Scan(){
	int ans=0;
	char k=getchar();
	while(!isdigit(k))k=getchar();
	while(isdigit(k))ans=ans*10+k-'0',k=getchar();
	return ans;
}
stack<int> t;
inline void Print(ll k){
	while(k)t.push(k%10),k/=10;
	while(!t.empty())putchar(t.top()+'0'),t.pop();
	putchar('\n');
}
inline void Neg(){
	putchar('-'),putchar('1'),putchar('\n');
}
int main(){
	int T,opt,d,a,b,cnt[2]={};
	ll t1a,t1b,t2a,t2b;
	T=Scan(),worst=base<<1;
	while(T--){
		opt=Scan(),d=Scan(),a=Scan(),b=Scan();
		if(opt==0){
			--s[d][gh(a,b)],--cnt[d];
			if(cnt[0]==0||cnt[1]==0){Neg();continue;}
			if(s[d][gh(a,b)]>0){Print(worst);continue;}
			worst=base<<1;
			for(auto i=s[0].begin();i!=s[0].end();i++){
				if(i->second==0)continue;
				for(auto j=s[1].begin();j!=s[1].end();j++){
					if(j->second==0)continue;
					rh(i->first,t1a,t1b),rh(j->first,t2a,t2b);
					//printf("%lld %lld %lld %lld\n",t1a,t2a,t1b,t2b);
					if(t1a+t2a>=worst)break;
					worst=min(worst,max(t1a+t2a,t1b+t2b));
				}
			}
		}else if(opt==1){
			++s[d][gh(a,b)],++cnt[d];
			if(cnt[0]==0||cnt[1]==0){Neg();continue;}
			if(s[d][gh(a,b)]>1){Print(worst);continue;}
			t1a=a,t1b=b;
			for(auto i=s[d^1].begin();i!=s[d^1].end();i++){
				if(i->second==0)continue;
				rh(i->first,t2a,t2b);
				//printf("%lld %lld %lld %lld\n",t1a,t2a,t1b,t2b);
				if(t1a+t2a>=worst)break;
				worst=min(worst,max(t1a+t2a,t1b+t2b));
			}
		}
		Print(worst);
	}
	return 0;
}
//-1
//105
//55
//43
//36
//48

期望得分 \(\text{140}\)
\(\text{10:59}\)

T4 20 区间dp

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll Mod=1e9+7;
const int N=1009;
ll dp[N][N];
int n,q;
int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)scanf("%lld",&dp[i][1]);
	for(int l=2;l<=n;l++){
		for(int i=1;i+l-1<=n;i++){
			for(int sep=i+1;sep<=i+l-1;sep++){
				dp[i][l]=max(dp[i][sep-i]+dp[sep][i+l-sep]*2,dp[i][l]);
			}
		}
	}
	int l,r;
	while(q--){
		scanf("%d%d",&l,&r);
		printf("%lld\n",dp[l][r-l+1]%Mod);
	}
	return 0;
}

期望得分 \(\text{160}\)
\(\text{11:08}\)

赛后补题部分

T1

哈希一本通练习原题
难度黄

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

//Data Part
typedef long long ll;
const ll Mod1=1e9+7,Mod2=1e9+9,Base=31;
const int N=2000099;
int n,mid;
char k[N];
ll h1[N],h2[N],ans1=-1,ans2=-1,lim=-1,rim=-1,Base1[N],Base2[N];

//Merge Part
inline void Add(ll lh1,ll lh2,ll rh1,ll rh2,int rlen,ll &Ans1,ll &Ans2){
	Ans1=(Base1[rlen]*lh1%Mod1+rh1)%Mod1;
	Ans2=(Base2[rlen]*lh2%Mod2+rh2)%Mod2;
}
inline ll Cut1(int l,int r){return ((h1[r]-h1[l-1]*Base1[r-l+1]%Mod1)%Mod1+Mod1)%Mod1;}
inline ll Cut2(int l,int r){return ((h2[r]-h2[l-1]*Base2[r-l+1]%Mod2)%Mod2+Mod2)%Mod2;}
int main(){
	scanf("%d%s",&n,k+1),mid=(n+1)>>1,Base1[0]=Base2[0]=1;
	if(n%2==0)puts("NOT POSSIBLE"),exit(0);
	for(int i=1;i<=n;i++){
		h1[i]=(h1[i-1]*Base+(k[i]-'A'))%Mod1;
		h2[i]=(h2[i-1]*Base+(k[i]-'A'))%Mod2;
		Base1[i]=Base1[i-1]*Base%Mod1;
		Base2[i]=Base2[i-1]*Base%Mod2;
	}
	ll lans1,lans2,rans1,rans2,tmplim,tmprim;
	for(int i=1;i<=n;i++){
		if(i<mid){
			rans1=Cut1(mid+1,n),rans2=Cut2(mid+1,n),tmplim=mid+1,tmprim=n;
			Add(Cut1(1,i-1),Cut2(1,i-1),Cut1(i+1,mid),Cut2(i+1,mid),mid-i,lans1,lans2);
		}else if(i==mid){
			lans1=Cut1(1,i-1),lans2=Cut2(1,i-1),tmplim=1,tmprim=mid-1;
			rans1=Cut1(i+1,n),rans2=Cut2(i+1,n);
		}else{
			lans1=Cut1(1,mid-1),lans2=Cut2(1,mid-1),tmplim=1,tmprim=mid-1;
			Add(Cut1(mid,i-1),Cut2(mid,i-1),Cut1(i+1,n),Cut2(i+1,n),n-i,rans1,rans2);
		}
		//printf("%d %lld %lld %lld %lld\n",i,lans1,rans1,lans2,rans2);
		if(lans1!=rans1||lans2!=rans2)continue;
		if(ans1==-1||ans2==-1)ans1=lans1,ans2=lans2,lim=tmplim,rim=tmprim;
		else if(ans1==lans1)continue;
		else puts("NOT UNIQUE"),exit(0);
	}
	if(ans1==-1||ans2==-1)puts("NOT POSSIBLE"),exit(0);
	for(int i=lim;i<=rim;i++)putchar(k[i]);
	return 0;
}

T2

题解说这题首先有一个思路
我们先搞出 \(i\) 轮完整的冒泡
然后在暴力的搞剩下的几个 \(num\)
那么现在我们就有了这么步骤

  1. 以较小的时间复杂度模拟一轮冒泡
  2. 判断当前还能搞几轮冒泡,需要几次 \(\text{swap}\)
  3. 得出剩下几个操作数量来暴力搞
    然后我们下一个问题就是: 如何模拟跑完后的数组

首先有一部分的东西是很唐的
他们可能 \(k\) 轮中每轮都被压一次
这种很简单
直接向左移动 \(k\) 就可以了

剩下的呢
根据我们上面的结论
我们

我们看个图

图中带红圈的就是我们会 "向后推" 的位置
我们可以发现几个规律

  1. 我们能 “向后推” 的位置都是单调递增的
  2. 向后推的过程会被下一个比她大的数卡住
  3. 如果一个数的左边有比它大的数,那他就不能向后推
  4. 一轮中一个数至多只被一个“向后推”的数撞到

我们这些东西就可以求出 \(k\) 轮冒泡的最小代价
首先,我们发现向后退的并不好处理
因为他们涉及到 "被卡" 这个恶心的东西。。。
所以我们可以统计每一个数字会被“撞”多少次

这个数是多少呢?
首先,他不会大于 \(k\)
谁家好人一轮就被创两次啊
其次,他不会大于这个数左边比他大的数的个数
因为只有他们才能创到他并轧过去
而我们当前的数如果向后推的话
他肯定无法压掉比它大的数
也就是说他左边比它大的数的数量不会增多
然后我们一波并不算玄学的推论
他被创到并被轧得次数就是 \(\min(k,cnt)\)
其中 \(cnt=\sum_{it=1}^{i-1} [k[it]>k[i]]\)
而容易看出
我们每个点被轧的次数
其实就是 \(\text{swap}\) 的次数
所以我们用树状数组维护每个点的 \(\text{cnt}\)
然后枚举 \(k\) 的值
得出可行的轮数
用玄学方式整出搞好的数组
然后最后一轮直接暴力

最后一个问题
怎么模拟?
首先我们发现有的数字很唐
他们在这 \(k\) 轮之中都会被压一次
然后他们的位置就是原位置减去 \(k\)

剩下的呢
由于他们左边根据定义已经没有比他们大的了
所以直接排序往里填
最后一个暴力
完结撒花!
难度绿至蓝

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

//Data Part
typedef long long ll;
const int N=1000009;
int n,tmp[N],a[N],cnt[N],Round; ll k;

namespace Merge_Sort{
	int ltmp[N],rtmp[N];
	ll ans=0;
	void Merge(int l=1,int r=n){
		if(l==r)return;
		int mid=(l+r)>>1;
		Merge(l,mid),Merge(mid+1,r);
		for(int i=l;i<=mid;i++)ltmp[i]=tmp[i];
		for(int i=mid+1;i<=r;i++)rtmp[i]=tmp[i];
		int L=l,R=mid+1,idx=l-1;
		while(L<=mid&&R<=r){
			if(ltmp[L]<rtmp[R])tmp[++idx]=ltmp[L++];
			else tmp[++idx]=rtmp[R++],ans+=mid-L+1;
		}
		while(L<=mid)tmp[++idx]=ltmp[L++];
		while(R<=r)tmp[++idx]=rtmp[R++];
	}
	void Check(){
		Merge();
		if(ans<k)puts("Impossible!"),exit(0);
	}
}
namespace Tree{
	int t[N]={};
	void Modify(int u){while(u<=n)t[u]++,u+=(u&-u);}
	int Query(int u){int ans=0;while(u>=1)ans+=t[u],u-=(u&-u);return ans;}
	void Cnt(){
		for(int i=1;i<=n;i++)cnt[i]=i-1-Query(a[i]),Modify(a[i]);
	}
}
namespace Binary{
	bool Check(int x,bool op){
		ll need=0;
		for(int i=1;i<=n;i++)need+=min(x,cnt[i]);
		if(op)k-=need;
		return need<=k;
	}
	void Solve(int l=1,int r=n){
		int mid;
		while(l<r){
			mid=(l+r+1)>>1;
			if(Check(mid,false))l=mid; else r=mid-1;
		}
		Round=l,Check(l,true);
	}
}
namespace Fill{
	vector<int> vec;
	void Solve(){
		for(int i=1;i<=n;i++) tmp[i]=0;
		for(int i=1;i<=n;i++) if(cnt[i]>=Round) tmp[i-Round]=a[i]; else vec.push_back(a[i]);
		sort(vec.begin(),vec.end());
		int idx=1;
		for(int i:vec){while(tmp[idx])++idx; tmp[idx]=i;}
	}
}
namespace Brute{
	void Solve(){
		for(int i=1;i<n;i++) if(tmp[i]>tmp[i+1]){
			if(k==0)return;
			--k,swap(tmp[i],tmp[i+1]);
		}
	}
}
int main(){
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&tmp[i]),a[i]=tmp[i];
	Merge_Sort::Check();
	Tree::Cnt();
	Binary::Solve();
	Fill::Solve();
	Brute::Solve();
	for(int i=1;i<=n;i++)printf("%d ",tmp[i]);
}
posted @ 2025-04-01 19:16  2025ing  阅读(34)  评论(0)    收藏  举报