「考試總結」模擬2
同桌的你
難寫但無意義的題目,所以直接鴿掉了
做法是一個簡單的二元組 \(\rm{DP}\),記錄最大獨立集和異性匹配數
環上的部分考慮最優解的形態
直接刪掉基環上的任意一條邊之後做 \(\rm{DP}\),如果其不在最優解上那麼可以得到答案,如果其在最優解上那麼必然相鄰環上的邊不在最優解上
所以找兩條刪刪就行了
大水題
先對序列求字首和
如果直接給定一個區間,判斷若干種牛出現次數出現次數相同的方式是取出這些種類的點值差分,如果差分表對應那全相同則出現次數相同
這種做法正確性不難自證,但直接應用到本題中,即使用 \(\rm{hash}\) 表維護狀態對應做端點 時間複雜度達到了 \(\Theta(\rm{maxb\ 2^{\rm{maxb}}n})\)
真的需要對所有的 \(2^{\rm{maxb}}\) 種 種類搭配方式都列舉一次嗎?
對於一個固定區間右端點,在左端點從右往左挪的過程中,牛出現的種類數只會變化 \(\rm{maxb}\) 次
那麼對於列舉的右端點,動態維護每個左端點到當前右端點的牛的出現集合
不難發現每次只需要修改 \([\rm{lst[a[i]],i-1}]\) 中的點作為左端點時的出現牛的狀態集合,伴隨狀態集合的變化,對應的差分表也會增加數字
這部分會遍歷 \(\rm{maxb}\) 次陣列,複雜度比較優秀
那麼在 unordered_map
中把不合法的狀態左端點改成 \(-1\),把當前的牛的集合加入即可
更新答案考慮按照上一次出現的位置增加進入狀態再去雜湊表裡面查即可
這樣的話複雜度是 \(\Theta(\rm{maxb^2n})\) 不可避免地帶了 \(8\) 的常數,但是比 \(256\) 好多了
$\texttt{Talk is cheap,Show the Code}$
#include<bits/stdc++.h> using namespace std; #define reg register #define int long long #define pb push_back #define Mp make_pair #define ll long long #define ull unsigned long long #define Down(i,a,b) for(reg int i=a;i>=b;--i) #define rep(i,a,b) for(reg int i=a;i<=b;++i) template<typename T>inline void ckmin(T &x,T y){x=x<y?x:y; return ;} template<typename T>inline void ckmax(T &x,T y){x=x<y?y:x; return ;} namespace yspm{ inline int read(){ int res=0,f=1; char k; while(!isdigit(k=getchar())) if(k=='-') f=-1; while(isdigit(k)) res=res*10+k-'0',k=getchar(); return res*f; } char OPUT[100]; inline void print(int x){ if(!x) return putchar('0'),putchar('\n'),void(); if(x<0) putchar('-'),x=-x; int cnt=0; while(x) OPUT[++cnt]=x%10,x/=10; Down(i,cnt,1) putchar(OPUT[i]+'0'); putchar('\n'); return ; } unordered_map<ull,int> mp; const int N=1e5+10; int n,ans=-1,v[10],sum[N][9],k,lst[N]; pair<int,int> w[N]; ull bas[10],tt; inline bool cmp(pair<int,int> a,pair<int,int> b){return a>b;} inline ull get(int pos,int st){ int fir=8; ull res=st; rep(i,1,8) if(st>>(i-1)&1){fir=i; break;} rep(i,fir+1,8) if(st>>(i-1)&1) res=res*bas[1]+sum[pos][i]-sum[pos][fir]; return res; } pair<int,int> tmp[10]; signed main(){ n=read(); k=read(); rep(i,1,n) w[i].first=read(),w[i].second=read(); sort(w+1,w+n+1); bas[0]=1; rep(i,1,8) bas[i]=bas[i-1]*13331; mp[0]=w[1].first; rep(i,1,n){ rep(j,1,8) sum[i][j]=sum[i-1][j]; sum[i][w[i].second]++; rep(j,lst[w[i].second],i-1){ int app=0; rep(k,1,8) if(sum[i-1][k]-sum[j][k]>0) app|=(1<<(k-1)); if(mp.count(tt=get(j,app))) mp[tt]=-1; app|=(1<<(w[i].second-1)); if(!mp.count(tt=get(j,app))) mp[tt]=w[j+1].first; } lst[w[i].second]=i; rep(j,1,8) tmp[j]=Mp(lst[j],j); sort(tmp+1,tmp+8+1,cmp); int sta=0; rep(j,1,k-1) sta|=1<<(tmp[j].second-1); for(reg int j=k;j<=8&&tmp[j].first;++j){ sta|=1<<(tmp[j].second-1); if(mp.count(tt=get(i,sta))&&mp[tt]!=-1) ckmax(ans,w[i].first-mp[tt]); } mp[0]=w[i+1].first; } print(ans); return 0; } }signed main(){return yspm::main();} //Use The Time To Enrich This Selfclosing Youth
佛羅里達
面向範圍程式設計可以發現正解複雜度在 \(\Theta(n^3)\) 左右,那麼直觀的想法就是列舉所有邊作為一個集合的最大邊
不妨設 \(\max(A)\le \max(B)\),那麼列舉 \(B\) 之後答案必然有單調性,
那麼先二分 \(A\) 的權值,然後判斷是否可行是一個明顯的 \(\rm{2-sat}\)
-
對於那些 \(v\le \max(A)\) 的邊沒有限制
-
對於那些 \(\max(A)<v\le \max(B)\) 的邊不能都在集合 \(A\) 中所以分別連向對方的逆命題即可
-
對於 \(v>\max(B)\) 的情況,那麼除了上面的邊以外,逆命題連反向邊
至此得到了 \(\Theta(n^4\log n)\) 的做法,基本功紮實的話可以一兩分鐘想到
和上一題一樣,請問:真的有必要列舉所有邊作為 \(\max(B)\) 嗎?
考慮對完全圖求一個最大生成樹,黑白染色後,如果 \(B\) 集合包含兩種顏色的點,那麼集合最大邊一定在最大生成樹上面
首先 \(B\) 集合包含的點在生成樹上距離不能都超過二,否則 \(\max(A)>\max(B)\)
如果距離都等於二那麼都是同一個顏色
至此證明了如果集合包含兩種顏色的點,那麼 \(\max(B)\) 一定在最大生成樹上
這樣子只需要列舉 \(O(n)\) 級別的邊就能覆蓋這部分,剩下的是在染色之後黑色點一個集合,白色一個,分別列舉即可
$\texttt{Talk is cheap,Show the Code}$
#include<bits/stdc++.h>
using namespace std;
#define reg register
#define pb push_back
#define mp make_pair
#define Down(i,a,b) for(reg int i=a;i>=b;--i)
#define rep(i,a,b) for(reg int i=a;i<=b;++i)
template<typename T>inline void ckmin(T &x,T y){x=x<y?x:y; return ;}
template<typename T>inline void ckmax(T &x,T y){x=x<y?y:x; return ;}
namespace yspm{
inline int read(){
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
char OPUT[100];
inline void print(int x){
if(!x) return putchar('0'),putchar('\n'),void();
if(x<0) putchar('-'),x=-x;
int cnt=0; while(x) OPUT[++cnt]=x%10,x/=10;
Down(i,cnt,1) putchar(OPUT[i]+'0'); putchar('\n'); return ;
}
const int N=510;
int num,dfn[N],v[N][N],tim,vis[N],scc,n,low[N],st[N],top,head[N],cnt,bel[N];
struct edge{int to,nxt;}e[N*N*4];
inline void adde(int u,int v){e[++cnt].nxt=head[u]; e[head[u]=cnt].to=v; return ;}
struct Edge{
int from,to,dis;
bool operator<(const Edge &a)const{return dis>a.dis;}
}ee[N*N];
inline void tarjan(int x){
dfn[x]=low[x]=++tim; vis[st[++top]=x]=1;
for(reg int i=head[x];i;i=e[i].nxt){
int t=e[i].to; if(!dfn[t]) tarjan(t),ckmin(low[x],low[t]); else if(vis[t]) ckmin(low[x],dfn[t]);
} if(dfn[x]==low[x]){
++scc; do bel[st[top]]=scc,vis[st[top]]=0,--top; while(st[top+1]!=x);
} return ;
}
int b[N*N*2],cv;
inline bool check(int va,int vb){
rep(i,1,n*2) head[i]=dfn[i]=vis[i]=low[i]=0; top=tim=cnt=0;
rep(i,1,n) rep(j,i+1,n){
if(v[i][j]<=va) continue;
if(v[i][j]<=vb) adde(i+n,j),adde(j+n,i);
else adde(i+n,j),adde(j+n,i),adde(i,j+n),adde(j,i+n);
}
rep(i,1,n) if(!dfn[i]) tarjan(i);
rep(i,1,n) if(bel[i]==bel[i+n]) return 0; return 1;
}
int fa[N],ans,col[N];
inline int find(int x){return fa[x]==x?fa[x]:fa[x]=find(fa[x]);}
vector<int> g[N];
inline void dfs(int x,int fat){
col[x]=col[fat]^1; for(auto t:g[x]) if(t!=fat) dfs(t,x); return ;
}
signed main(){
while(~scanf("%d",&n)){
rep(i,1,n) fa[i]=i,g[i].clear(),col[i]=0; cv=num=0; ans=2e9; b[++cv]=0;
rep(i,1,n) rep(j,1,n-i) b[++cv]=v[i][j+i]=v[j+i][i]=read(),ee[++num]=(Edge){i,j+i,v[i][i+j]};
sort(ee+1,ee+num+1); sort(b+1,b+cv+1); cv=unique(b+1,b+cv+1)-b-1;
rep(i,1,num) ee[i].dis=lower_bound(b+1,b+cv+1,ee[i].dis)-b;
rep(i,1,num){
int x=ee[i].from,y=ee[i].to; if(find(x)==find(y)) continue;
fa[find(x)]=find(y); if(!check(b[ee[i].dis],b[ee[i].dis])) continue;
int vl=1,vr=ee[i].dis-1,res=ee[i].dis; g[x].pb(y); g[y].pb(x);
while(vl<=vr){
int mid=(vl+vr)>>1;
if(check(b[mid],b[ee[i].dis])) res=mid,vr=mid-1; else vl=mid+1;
}
ckmin(ans,b[res]+b[ee[i].dis]);
}
int mx1=0,mx2=0; dfs(1,0);
rep(i,1,n) rep(j,i+1,n) if(col[i]^col[j]) continue; else{
if(col[i]) ckmax(mx1,v[i][j]);
else ckmax(mx2,v[i][j]);
} print(min(ans,mx1+mx2));
} return 0;
}
}signed main(){return yspm::main();}
//Use The Time To Enrich This Selfclosing Youth