1. 程式人生 > 實用技巧 >「NOIP第四階段」整理

「NOIP第四階段」整理

模擬賽45

考試時犯困,T3暴力都沒打

T1: bins

一眼沙雕題,指標+桶掃一遍就行了

T2:inversions

歸併沒學兩行淚

由於沒學歸併,考場就沒往正解上想

考慮一個區間翻轉對其它區間的影響

  • 對本級及含於本級的區間,逆序對個數與正序對個數交換

  • 對於同級其他及本級以上,當前區間沒影響

所以直接用歸併統計出每一級區間的正逆序對個數,修改時打標記即可



#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rint register int
#define ll long long
#define ull unsigned long long
#define Max(a,b) (a>b?a:b)
using namespace std;
const int maxn=1e6+5e5+5,INF=0x3f3f3f3f;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
ull k1,k2;
int a[maxn],q[maxn],flag;
inline ull xorShift128Plus(){
	ull k3=k1,k4=k2;
	k1=k4;
	k3^=(k3<<23);
	k2=k3^k4^(k3>>17)^(k4>>26);
	return k2+k4;
}
int n,m,threshold,b[maxn],c[maxn];
bool vis[maxn];
ll ans,now,f[22],g[22];
#define mid ((l+r)>>1)
void merge_sort(int l,int r){
	if(l==r)return;
	merge_sort(l,mid);merge_sort(mid+1,r);
	int idl=l,idr=mid+1,id=l-1,now=log2(r-l+1);
	while(idl<=mid&&idr<=r){
		if(a[idl]<=a[idr]){
			b[++id]=a[idl];
			idl++;
		}else{
			b[++id]=a[idr];
			if(flag==1)g[now]+=mid-idl+1;
			else f[now]+=mid-idl+1;
			idr++;
		}
	}
	while(idl<=mid)b[++id]=a[idl++];
	while(idr<=r)b[++id]=a[idr++];
	for(id=l;id<=r;++id)a[id]=b[id];
}
signed main(){
	freopen("inversions.in","r",stdin);
	freopen("inversions.out","w",stdout);
	n=read(),m=read(),threshold=read(),cin>>k1>>k2;
	int maxs=1<<n;
	for(rint i=1;i<=maxs;++i)a[i]=xorShift128Plus()%threshold+1;
	for(rint i=1;i<=m;++i)q[i]=xorShift128Plus()%(n+1);
	n=(1<<n);
	memcpy(c,a,sizeof(a));flag=1;
	merge_sort(1,n);
	memcpy(a,c,sizeof(c));flag=2;
	reverse(a+1,a+1+n);
	merge_sort(1,n);
	n=log2(n);
	for(rint i=1;i<=m;++i){
		ll now=0;
		for(rint j=1;j<=n;++j){
			if(j<=q[i])vis[j]^=1;
			if(vis[j])now+=f[j];//,cout<<f[j]<<" ";
			else now+=g[j];//,cout<<g[j]<<" ";
		}
		ans^=(now*i);
	}
	printf("%lld\n",ans);
	return 0;
}

T3:candies

送的50pts沒打,awsl

第一問相當於消失之物,考場忘了消失之物咋寫硬搞還搞錯了

具體點,取 \(q\) 為 正無限,那當前能表示的數就可以翻倍

所以直接消失之物找最多表示

第二問就有。意思了,

對於不合法的 \(q\) ,顯然有

\[S + q = T (S,T均為可表示的集合) \]

移項

\[q = T - S \]

發現就是一個權值可正可負的揹包,所以再跑一遍揹包找出最大不能表示的數

負下標寫法是從liu_run_da某篇題解裡學的

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rint register int
#define ll long long
#define ull unsigned long long
#define Max(a,b) (a>b?a:b)
using namespace std;
const int maxn=7e5+5,INF=0x3f3f3f3f,mol=1e9+7,base=7e5;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int DP[maxn*2],f1[maxn],maxid,ans,a[maxn],n,sum,f3[maxn],sum1[maxn];
int *f2=DP+base;
int main(){
	freopen("candies.in","r",stdin);
	freopen("candies.out","w",stdout);
	n=read();
	for(rint i=1;i<=n;++i)a[i]=read(),sum+=a[i];
	sort(a+1,a+1+n);
	f1[0]=1;
	for(rint i=1;i<=n;++i){
		for(rint j=sum;j>=a[i];--j){
			f1[j]+=f1[j-a[i]];
			if(f1[j]>mol)f1[j]-=mol;
		}
	}
	sum1[0]=1;
	for(rint i=1;i<=sum;++i)sum1[i]=sum1[i-1]+(f1[i]>=1);
	memcpy(f3,f1,sizeof(f1));
	for(rint i=1;i<=n;++i){
		for(rint j=0;j<=sum;++j)f1[j]=f3[j];
		int cnt=sum1[a[i]-1];
		for(rint j=a[i];j<=sum;++j){
			if(f1[j-a[i]]){
				f1[j]-=f1[j-a[i]];
				if(f1[j]<0)f1[j]+=mol;
			}
			if(f1[j])cnt++;
		}
		if(cnt>ans)ans=cnt,maxid=i;
	}
	printf("%d ",a[maxid]);
	f2[0]=1;
	for(rint i=1;i<=n;++i){
		if(i==maxid)continue;
		for(rint j=sum;j>=-sum;--j){
			if(j-a[i]>=-sum)f2[j]|=f2[j-a[i]];
		}
		for(rint j=-sum;j<=sum;++j){
			if(j+a[i]<=sum)f2[j]|=f2[j+a[i]];
		}
	}
	for(rint j=0;j<=sum+a[maxid]+1;++j){
		if(!f2[j]){printf("%d\n",j);break;}
	}
	return 0;
}

T4:sheep

傻逼題給爺爬

晚間小測10

T1:跳躍

前幾天剛考過的折半列舉

發現折半後的一個狀態合法有兩個條件:

  • \(w1 + w2 >= m\)

  • 前一半尾位不大於後一半首位

前一半直接排序,後一半對每一個首位存狀態,vector內部按權值排序,再記錄一個指標

由於合法狀態一定不滿,空間最大不會超過64MB

另外沒事少開 #define int long long(指某人MLE了)

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rint register int
#define ll long long
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
const int maxn=1e6+5e5+5,INF=0x3f3f3f3f;
using namespace std;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int n,a[45],b[45],id[45],tot,tot1;
ll m,ans;
struct Node{
	int s,x;
	ll w;
	bool operator <(const Node &A)const{
		return w==A.w?x<A.x:w<A.w;
	}
}q1[maxn];
struct Nodee{
	int s,x;
	ll w;
	Nodee(int _s,int _x,ll _w){s=_s,x=_x,w=_w;}
	bool operator <(const Nodee &A)const{
		return w==A.w?x<A.x:w<A.w;
	}
};
vector<Nodee> g[45];
int main(){
	freopen("san.in","r",stdin);
	freopen("san.out","w",stdout);
	n=read();scanf("%lld",&m);
	for(rint i=1;i<=n;++i)a[i]=read(),b[i]=read();
	int siz=n/2,maxs1=(1<<siz)-1,maxs2=(1<<n-siz)-1,now;
	ll sum;
	for(rint s=1;s<=maxs1;++s){
		now=0;sum=0;
		int last=0;
		for(rint i=1;i<=siz;++i){
			if(s&(1<<i-1)){
				if(a[i]<now)goto skr1;
				now=a[i];
				sum+=1LL*b[i];
				last=i;
			}
		}
		if(sum>=m)ans++;
		q1[++tot1]=(Node){s,last,sum};
		skr1:;
	}
	for(rint s=1;s<=maxs2;++s){
		now=0;sum=0;
		int fst=0;
		for(rint i=1;i<=n-siz;++i){
			if(s&(1<<i-1)){
				if(!fst)fst=i;
				if(a[i+siz]<now)goto skr2;
				now=a[i+siz];
				sum+=1LL*b[i+siz];
			}
		}
		if(sum>=m)ans++;
		g[fst].push_back(Nodee(s,fst,sum));
		skr2:;
	}
	sort(q1+1,q1+1+tot1);
	for(rint i=1;i<=n-siz;++i){
		sort(g[i].begin(),g[i].end());
		id[i]=g[i].size();
//		for(rint j=0;j<g[i].size();++j)cout<<i<<" "<<g[i][j].w<<" "<<g[i][j].x<<endl;
	}
	for(rint i=1;i<=tot1;++i){
		for(rint j=1;j<=n-siz;++j){
			if(a[j+siz]<a[q1[i].x]||g[j].empty())continue;
			while(id[j]&&g[j][id[j]-1].w+q1[i].w>=m)id[j]--;
			//cout<<i<<" "<<j<<" "<<id[j]<<" "<<g[j].size()<<" "<<ans<<endl;
			ans+=(int)g[j].size()-id[j];
//				cout<<i<<" "<<q1[i].x<<" "<<g[j][id[j]].w<<" j="<<j<<" "<<id[j]<<" "<<g[j].size()<<" ans="<<ans<<endl;
		}
	}
	printf("%lld\n",ans);
	return 0;
}

T2:床單

考場用最後的五分鐘將33pts的程式碼改成了13pts,原因是:

我理解成:若打到了小床單上,小床單下的大床單即使沒為射擊點上也會被染色,大床單染色又會下傳,然後我就建了個圖,無了

正解沒寫


模擬測試46


考場發現T4尤拉定理可衝,發現模數轉尤拉函式後還不是質數,沒往CRT上想就走了

兩個小時後發現自己T1還沒調出來,看到左右做題速度飛快,果斷上個坼鎖開下一道題

最後一個小時衝T1 \(O(nlogn^2)\)

改完後在更