11.9 正睿做題筆記
阿新 • • 發佈:2021-11-09
T1
這個題的一個想法是我們首先把所有點放入堆裡,然後每次從堆中取出權值和最小的團,然後往這個團裡每次插入一個可以插的點。但是這樣放入堆裡的元素個數無法估計,會被卡空間,下面有幾種優化:
- 首先我們把堆換成 multiset,然後如果取出的元素個數加上集合內元素個數之和大於 \(k\),我們就可以把那些最大的刪掉。另一個優化是如果當前元素大於 \(k\) 並且當前插入值比最大值大,我們就不插入。
- 我們首先把所有點的權值排序,然後我們每次取出一個團,有兩種操作,第一種是去掉最大的那個點,換一個更大的點。或者我們直接往這個團里加一個能加的點中最小的點。
- 我們換一種思路,考慮二分這個值,然後考慮是否能找到 \(k\)
__int128
來狀壓哪些點是可以加進當前團的。
程式碼用的是第一種實現方法。
程式碼:
#include<iostream> #include<bits/stdc++.h> #include<cstdio> #include<algorithm> #include<bitset> #include<queue> #define ll long long #define N 110 using namespace std; bitset<110> v[N]; template<typename T> inline void read(T &x){ x=0;int flag=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') flag*=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=flag; } struct Node{ ll val; bitset<110> bit; inline Node(){} inline bool operator < (const Node &b)const{ return val<b.val; } }; multiset<Node> S; int n,k,a[N],cnt; char s[N]; inline void Init(){ read(n);read(k);k--; for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++){ scanf("%s",s+1); for(int j=1;j<=n;j++){ v[i][j]=s[j]-'0'; } } for(int i=1;i<=n;i++){ Node now;now.bit.reset(); now.bit[i]=1;now.val=a[i]; S.insert(now); } } int main(){ // freopen("my.in","r",stdin); // freopen("my.out","w",stdout); Init(); while(S.size()){ Node Top=(*S.begin());S.erase(S.begin()); cnt++; if(cnt==k){ printf("%lld",Top.val);return 0; } int posi=-1; for(int i=1;i<=n;i++) if(Top.bit[i]) posi=i+1; while(S.size()+cnt>k) S.erase(--S.end()); for(int j=posi;j<=n;j++){ if((v[j]&Top.bit)==Top.bit&&(!(S.size()+cnt>k&&Top.val+a[j]>=(*(--S.end())).val))){ Top.bit[j]=1;Top.val+=a[j]; S.insert(Top); Top.bit[j]=0;Top.val-=a[j]; } } } puts("-1"); return 0; }
T2
我們首先考慮 \(a_i\) 一定是 \(b_{i-2},b_{i-1},b_i\) 中的一個值,原因是我們只是需要一個相對大小,我們總可以調整調整這個值使其變成 \(b\) 中的值,當然是有解的情況下。所以我們每個位置都有三個取值。設 \(f_{i,j,k}\) 表示 \(i\) 這個數放第 \(i\) 個取值,\(j\) 這個數放第 \(k\) 個取值,是否有解,轉移即可。程式碼實現略有技巧。
程式碼:
#include<bits/stdc++.h> #define dd double #define ld long double #define ll long long #define uint unsigned int #define ull unsigned long long #define N 100010 #define M number using namespace std; const int INF=0x3f3f3f3f; template<typename T> inline void read(T &x) { x=0; int f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c == '-') f=-f; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } inline int GetMid(int a,int b,int c){ if(a>b) swap(a,b);if(b>c) swap(b,c);if(a>b) swap(a,b); return b; } int b[N],t,n,a[N][3],Pre[N][3][3],ans[N]; bool f[N][3][3]; inline void Clear(){ for(int i=1;i<=n;i++){ for(int j=0;j<=2;j++) for(int k=0;k<=2;k++){ f[i][j][k]=0;Pre[i][j][k]=-1; } } } inline void Init(){ read(n); for(int i=3;i<=n;i++) read(b[i]); b[1]=b[2]=b[3]; b[n+1]=b[n+2]=b[n]; for(int i=1;i<=n;i++){ a[i][0]=b[i];a[i][1]=b[i+1];a[i][2]=b[i+2]; // printf("a[%d][0]=%d a[%d][1]=%d a[2][%d]=%d\n",i,a[i][0],i,a[i][1],i,a[i][2]); } for(int i=1;i<=2;i++) for(int j=0;j<=2;j++) for(int k=0;k<=2;k++) f[i][j][k]=1; } inline void Solve(){ for(int i=3;i<=n;i++) for(int j=0;j<=2;j++) for(int k=0;k<=2;k++){ if(!f[i-1][k][j]) continue; for(int l=0;l<=2;l++){ if(GetMid(a[i][l],a[i-1][k],a[i-2][j])!=b[i]) continue; f[i][l][k]=1;Pre[i][l][k]=j; // printf("f[%d][%d][%d]=%d\n",i-1,k,j,f[i-1][k][j]); // printf("f[%d][%d][%d]=%d\n",i,l,k,f[i][l][k]); // printf("Pre[%d][%d][%d]=%d\n",i,l,k,Pre[i][l][k]); } } int nowj=-1,nowk=-1; for(int j=0;j<=2;j++) for(int k=0;k<=2;k++) if(f[n][j][k]){nowj=j;nowk=k;break;} // printf("nowj=%d nowk=%d\n",nowj,nowk); if(nowj==-1&&nowk==-1) return(void)puts("-1"); int now=n; while(nowj!=-1&&nowk!=-1){ ans[now]=a[now][nowj]; // printf("a[%d][%d]=%d\n",now,nowj,a[now][nowj]); int Next=Pre[now][nowj][nowk];nowj=nowk;nowk=Next;now--; // printf("Next=%d\n",Next); if(Next==-1){ ans[now]=a[now][nowk];break; } } ans[1]=b[1]; for(int i=1;i<=n;i++) printf("%d ",ans[i]);puts(""); } int main(){ // freopen("my.in","r",stdin); // freopen("my.out","w",stdout); memset(f,0,sizeof(f));memset(Pre,-1,sizeof(Pre)); read(t); while(t--){ Init();Solve();Clear(); } return 0; }
T3,T4 因為考得不是 NOIP 內容,所以暫且先不補了。