4.8省選模擬
阿新 • • 發佈:2022-04-08
信心賽\(?\)
考場上期望\(60+100+[0,30]\)
實際\(70+100+30=200\)
\(T1\)
考場上想到\(60\)分的區間\(dp\)就放棄夢想了。。。(不要學我)
正解是\(PAM\)上跑\(dp,\)當然了,在校內資料上,直接\(Manacher\)跑\(dp\)就可以了
本質上和區間\(dp\)一樣
#define Eternal_Battle ZXK #include<bits/stdc++.h> #define int long long #define MAXN 1000005 using namespace std; char s[MAXN],Mid[MAXN]; int len[MAXN],tmp[MAXN],LEN[MAXN]; void Init() { int cnt=-1; s[++cnt]='#'; for(int i=0;Mid[i];i++) { s[++cnt]='#'; s[++cnt]=Mid[i]; } s[++cnt]='#'; } void Manache() { int zj=0,Max=0; for(int i=0;s[i];i++) { if(i<=Max) len[i]=min(Max-i,len[2*zj-i]); while(s[i+len[i]]==s[i-len[i]]) len[i]++; if(i+len[i]>Max) { Max=i+len[i]; zj=i; } } } int solve(int l,int r) { if(l>r) return 0; if(l==r) return 1; int now=r-l+1,res=r-l+1; for(int i=l;i<=r;i++) { tmp[i]=min(LEN[i],min(r-i,i-l+1)); } for(int i=l;i<=r;i++) { if(res<=now-2*tmp[i]) continue; res=min(res,now-2*tmp[i]+(tmp[i]>0)+solve(i-tmp[i]+1,i)); } return res; } void sol() { // memset(len,0,sizeof(len)); scanf("%s",Mid); Init(); Manache(); int cnt=0; for(int i=3;s[i];i+=2) { LEN[++cnt]=(len[i]-1)>>1; // cout<<LEN[cnt]<<" "; } // cout<<"\n"; printf("%lld\n",solve(1,strlen(Mid))); } int T; signed main() { freopen("dna.in","r",stdin); freopen("dna.out","w",stdout); scanf("%lld",&T); while(T--) sol(); }
\(T2\)
考場上不會正解,觀察大樣例發現,這種造資料方式造的資料應該不強...
在\(rand()\)一個前面點作為父親的期望樹高為\(log(n),\)那麼就不必要想正解了,直接暴力修改鏈就好了...
大樣例\(1.5s\)沒事,卡常卡到了\(0.5s\)(真就大常數選手),然後出題人誠不欺我,就過了
#include<bits/stdc++.h> #define rs ((now<<1)|1) #define ll long long #define MAXN 200005 #define ls (now<<1) using namespace std; template <typename T> inline void read (T &a) { T x = 0, f = 1; char ch = getchar (); while (! isdigit (ch)) { (ch == '-') and (f = 0); ch = getchar (); } while (isdigit (ch)) { x = (x << 1) + (x << 3) + (ch xor '0'); ch = getchar (); } a = f ? x : -x; } struct Tree { ll sum,add; }tr[MAXN<<2]; int head[MAXN],nxt[MAXN],val[MAXN],to[MAXN],up[MAXN],Tim,tot,n,m; int top[MAXN],dep[MAXN],siz[MAXN],son[MAXN],id[MAXN],bc[MAXN],f[MAXN]; bool vis[MAXN]; ll dis[MAXN],del[MAXN]; inline void add(int u,int v,int w) { tot++; to[tot]=v; val[tot]=w; nxt[tot]=head[u]; head[u]=tot; } inline void dfs_pre(int now,int fa) { int maxn=-1; dep[now]=dep[fa]+1; siz[now]=1; f[now]=fa; for(int i=head[now];i;i=nxt[i]) { int y=to[i]; if(y==fa) continue; dis[y]=dis[now]+val[i]; dfs_pre(y,now); siz[now]+=siz[y]; if(siz[y]>maxn) { maxn=siz[y]; son[now]=y; } } } inline void dfs_top(int now,int topn) { top[now]=topn; id[now]=++Tim; bc[Tim]=now; if(!son[now]) return ; dfs_top(son[now],topn); for(int i=head[now];i;i=nxt[i]) { int y=to[i]; if(top[y]) continue; dfs_top(y,y); } } inline void pd(int now,int L,int R) { if(tr[now].add) { ll Add=tr[now].add; tr[ls].add+=Add; int mid=(L+R)>>1; tr[ls].sum+=(ll)(mid-L+1)*Add; tr[rs].add+=Add; tr[rs].sum+=(ll)(R-(mid+1)+1)*Add; tr[now].add=0; } } inline void change(int now,int L,int R,int l,int r,int k) { if(L>=l&&R<=r) { tr[now].sum+=(ll)(R-L+1)*k; tr[now].add+=k; return ; } pd(now,L,R); int mid=(L+R)>>1; if(l<=mid) change(ls,L,mid,l,r,k); if(r>mid) change(rs,mid+1,R,l,r,k); } inline void change_lca(int x) { int lca=x,lst=0; // cout<<"OK"; while(lca) { // cout<<"lca: "<<lca<<"\n"; for(int i=head[lca];i;i=nxt[i]) { int y=to[i]; if(y==lst) continue; change(1,1,n,id[y],id[y]+siz[y]-1,2*dis[lca]); } del[lca]+=2*dis[lca]; lst=lca; lca=f[lca]; } } inline ll query(int now,int L,int R,int poz) { if(L==poz&&R==poz) { return tr[now].sum; } pd(now,L,R); int mid=(L+R)>>1; if(poz<=mid) return query(ls,L,mid,poz); else return query(rs,mid+1,R,poz); } signed main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); scanf("%d%d",&n,&m); for(int i=2;i<=n;i++) { read(up[i]); // scanf("%lld",&up[i]); up[i]++; } for(int i=2,w;i<=n;i++) { // scanf("%lld",&w); read(w); add(up[i],i,w); } // printf("%.3f",(double)clock()/CLOCKS_PER_SEC); dfs_pre(1,0); dfs_top(1,1); // build(1,1,n); int Num=0; ll Sdp=0; for(int i=1,x,opt;i<=m;i++) { // scanf("%lld%lld",&opt,&x); read(opt); read(x); x++; if(opt==1) { if(vis[x]) continue; vis[x]=true; Num++; Sdp+=dis[x]; change_lca(x); } else { ll Ans=(ll)Num*dis[x]+Sdp-query(1,1,n,id[x])-del[x]; printf("%lld\n",Ans); } } // printf("%.3f",(double)clock()/CLOCKS_PER_SEC); }
正解的話,也比較簡單,直接把暴力改鏈的過程改成打標記就好了,不就\(O(nlog^2n)\)和\(O(nlog^3n)\)的差距嗎,\(/xyx\)
\(T3\)
狀壓\(dp\)
考場上亂搞一波,拿到\(30...\)(亂搞\(yyds\))
\(dp[x][y][S]\)表示目前在\((x,y)\)節點,已經把\(S\)包圍起來的最小移動步數
最後答案就是\(val[S]-dp[sx][sy][S]\)
怎麼判斷一個點有沒有在裡面,直接判斷向上的線穿過幾次即可
有環的\(dp(guass?)\)
當然是最短路跑\(dp\)就好了
#include<bits/stdc++.h> #define INF 2147483647 #define MAXN 30 using namespace std; int dp[MAXN][MAXN][1<<10],gx[MAXN],gy[MAXN],val[MAXN]; int n,m,t,sx,sy,g[MAXN][MAXN],sum[1<<10]; int dx[4]={0,0,-1,1}; int dy[4]={1,-1,0,0}; struct nade { int x,y,sta; }; bool in(int x,int y,int xx,int yy,int i) { if(xx==gx[i]&&yy<gy[i])if(x<xx)return 1; if(x==gx[i]&&y<gy[i])if(x>xx)return 1; return 0; } char s[MAXN][MAXN]; queue<nade>que; int bfs() { que.push(nade{sx,sy,0}); int ans=0; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { for(int sta=0;sta<(1<<t);sta++) { dp[i][j][sta]=-1; } } } dp[sx][sy][0]=0; while(!que.empty()) { nade now=que.front(); que.pop(); int x=now.x,y=now.y,sta=now.sta; if(x==sx&&y==sy)ans=max(ans,sum[sta]-dp[x][y][sta]); for(int i=0;i<4;i++) { int xx=x+dx[i],yy=y+dy[i]; if(xx<1||xx>n||yy<1||yy>m||(s[xx][yy]!='.'&&s[xx][yy]!='S'))continue; int chichi=sta; for(int j=1;j<=t;j++) { if(in(x,y,xx,yy,j))chichi^=1<<(j-1); } if(dp[xx][yy][chichi]==-1) { dp[xx][yy][chichi]=dp[now.x][now.y][sta]+1; que.push(nade{xx,yy,chichi}); } } } return ans; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%s",s[i]+1); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { char ch=s[i][j]; if(ch=='S') { sx=i; sy=j; } else if(ch!='#'&&ch!='.'&&ch!='B') { gx[ch-48]=i; gy[ch-48]=j; t++; } } } for(int i=1;i<=t;i++) scanf("%d",&val[i]); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { char ch=s[i][j]; if(ch=='B') { gx[++t]=i; gy[t]=j; val[t]=-INF; } } } for(int sta=1;sta<(1<<t);sta++) { for(int i=1;i<=t;i++) { if(sta&(1<<i-1))sum[sta]+=val[i]; } } printf("%d\n",bfs()); }