「NOIP第四階段」整理
阿新 • • 發佈:2020-12-03
模擬賽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)\)
改完後在更