闲扯
今天原题大赛??
三道题,三个巨佬做过,还有一个昨晚刚做的。。。。
题面
\(T1\)
Solution
没学过回文自动机,咕咕咕。
\(T2\)
Solution
解法一:动态淀粉质
解法二:树剖 \(+\) 线段树
解法三:CDQ \(+\) 虚树 \(+\) \(DP\)
解法四:线段树 \(+\) 乱搞
前面三种解法都是大佬们想出来的,复杂度都是 \(O(n\log^2 n)\) 。最后一个是蒟蒻我在想不出正解的情况下乱搞的,没想到过了。。。
果然学校的考试数据很水啊。。
极限的话会被卡到 \(O(\frac{n^2}{2}\log n)\) ,但是随即数据下树的深度是 \(\log n\) 的,所以随机情况下是 \(n\log^2 n\) 。
具体解法还是咕了吧,巨佬们随便看看就懂了
Code
#include<bits/stdc++.h> #define del(a,i) memset(a,i,sizeof(a)) #define ll long long #define inl inline #define il inl void #define it inl int #define ill inl ll #define re register #define ri re int #define rl re ll #define mid ((l+r)>>1) #define lowbit(x) (x&(-x)) #define INF 0x3f3f3f3f using namespace std; template<class T>il read(T &x){ int f=1;char k=getchar();x=0; for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1; for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0'; x*=f; } template<class T>il print(T x){ if(x/10) print(x/10); putchar(x%10+'0'); } ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;} it qpow(int x,int m,int mod){ int res=1,bas=x%mod; while(m){ if(m&1) res=(res*bas)%mod; bas=(bas*bas)%mod,m>>=1; } return res%mod; } const int MAXN = 1e5+1; int n,m,f[MAXN],d[MAXN],head[MAXN],num_edge,opt,u,rt,mn=INF,dis[MAXN],sz[MAXN],dfn[MAXN],tot; struct Edge{ int next,to,dis; Edge(){} Edge(int next,int to,int dis):next(next),to(to),dis(dis){} }edge[MAXN<<1]; il add_edge(int u,int v,int dis){ edge[++num_edge]=Edge(head[u],v,dis),head[u]=num_edge; edge[++num_edge]=Edge(head[v],u,dis),head[v]=num_edge; } bool tr[MAXN],vis[MAXN]; ill BFS(int u){ rl res=0;del(vis,0); queue<pair<int,int> > q;q.push(make_pair(u,0)); while(!q.empty()){ pair<int,int> tmp=q.front();q.pop(); ri pos=tmp.first,dis=tmp.second; vis[pos]=1; if(tr[pos]) res+=dis; for(ri i=head[pos];i;i=edge[i].next){ if(vis[edge[i].to]) continue; q.push(make_pair(edge[i].to,dis+edge[i].dis)); } } return res; } il find_rt(int u,int fa){ sz[u]=1;ri mx=0; for(ri i=head[u];i;i=edge[i].next){ if(edge[i].to==fa) continue; find_rt(edge[i].to,u); sz[u]+=sz[edge[i].to]; mx=max(mx,sz[edge[i].to]); } mx=max(mx,n-sz[u]); if(mx<mn) mn=mx,rt=u; } il DFS(int u,int fa){ dfn[u]=++tot,sz[u]=1,f[u]=fa; for(ri i=head[u];i;i=edge[i].next){ if(edge[i].to==fa) continue; dis[edge[i].to]=dis[u]+edge[i].dis; DFS(edge[i].to,u),sz[u]+=sz[edge[i].to]; } } #define lc (cur<<1) #define rc (cur<<1|1) struct Seg_Tree{ ll sum,cnt; }T[MAXN<<2]; il pushup(int cur){T[cur].sum=T[lc].sum+T[rc].sum,T[cur].cnt=T[lc].cnt+T[rc].cnt;} il updata(int cur,int l,int r,int pos,int k){ if(l==r) T[cur].cnt=1,T[cur].sum=k; else{ if(mid>=pos) updata(lc,l,mid,pos,k); else updata(rc,mid+1,r,pos,k); pushup(cur); } } it query_cnt(int cur,int l,int r,int L,int R){ if(l>=L&&r<=R) return T[cur].cnt; ri res=0; if(mid>=L) res+=query_cnt(lc,l,mid,L,R); if(R>mid) res+=query_cnt(rc,mid+1,r,L,R); return res; } ill solve(int u){ ll res=T[1].sum+1ll*dis[u]*T[1].cnt; ri pos=f[u],size=query_cnt(1,1,n,dfn[u],dfn[u]+sz[u]-1); res-=2ll*dis[u]*size; while(pos){ ri siz=query_cnt(1,1,n,dfn[pos],dfn[pos]+sz[pos]-1); res-=2ll*dis[pos]*(siz-size); size=siz,pos=f[pos]; } return res; } int main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); read(n),read(m); for(ri i=1;i<n;++i) read(f[i]); for(ri i=1;i<n;++i) read(d[i]); for(ri i=1;i<n;++i) add_edge(i+1,f[i]+1,d[i]); if(n<=1000&&m<=1000){ for(ri i=1;i<=m;++i){ read(opt),read(u),++u; if(opt==1) tr[u]=1; else print(BFS(u)),puts(""); } } else{ find_rt(1,0),DFS(rt,0); for(ri i=1;i<=m;++i){ read(opt),read(u),++u; if(opt==1) updata(1,1,n,dfn[u],dis[u]); else print(solve(u)),puts(""); } } return 0; }
\(T3\)
Solution
题目中已经告诉我们怎么判断一个格子是否在多边形内部,这个判断条件只跟穿过次数奇偶性有关.
可以利用它进行状压 \(dp\).
给每个格子选一条射线,在转移的过程中更新有宝藏和陷阱的格子那条射线穿过边的奇偶性状态.
\(f(i,j,S)\) 表示当前在格子 \((i,j)\) ,宝藏和陷阱的射线奇偶性状态为 \(S\) 时,多边形最小周长.
转移时直接 \(bfs\) ,最后枚举哪些宝藏被圈了起来,统计答案.
Code
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; #define maxn 25 #define maxd 10 #define INF 0x7fffff template <typename T>inline T read() { register T sum=0; register char cc=getchar(); int sym=1; while(cc!='-'&&(cc>'9'||cc<'0'))cc=getchar(); if(cc=='-')sym=-1,cc=getchar(); sum=sum*10+cc-'0'; cc=getchar(); while(cc>='0'&&cc<='9')sum=sum*10+cc-'0',cc=getchar(); return sym*sum; } template <typename T>inline T read(T &a) { a=read<T>(); return a; } template <typename T,typename... Others> inline void read(T& a, Others&... b) { a=read(a); read(b...); } struct Node { int x; int y; int s; Node(int a=0,int b=0,int c=0) { x=a; y=b; s=c; } }pos[maxn]; int n,m,d,ans,limit,sx,sy,w[maxd]; int inque[maxn][maxn][1<<maxd],dis[maxn][maxn][1<<maxd]; const int mx[]={1,-1,0,0},my[]={0,0,1,-1}; char mp[maxn][maxn]; int pd(int x,int y,int nx,int ny,int S) { for(int i=1;i<=d;i++) if(x<pos[i].x&&((y<pos[i].y&&ny>=pos[i].y)||(y>=pos[i].y&&ny<pos[i].y))) S^=1<<(i-1); return S; } void SPFA() { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<limit;k++) dis[i][j][k]=INF; queue<Node>Q; Q.push(Node(sx,sy,0)); dis[sx][sy][0]=0; inque[sx][sy][0]=true; while(!Q.empty()) { Node tmp=Q.front();Q.pop(); inque[tmp.x][tmp.y][tmp.s]=false; for(int op=0;op<4;op++) { int nx=tmp.x+mx[op],ny=tmp.y+my[op]; if(nx<1||nx>n||ny<1||ny>m||mp[nx][ny]!='.') continue; int s=pd(tmp.x,tmp.y,nx,ny,tmp.s); if(dis[nx][ny][s]>dis[tmp.x][tmp.y][tmp.s]+1) { dis[nx][ny][s]=dis[tmp.x][tmp.y][tmp.s]+1; if(!inque[nx][ny][s]) { Q.push(Node(nx,ny,s)); inque[nx][ny][s]=true; } } } } for(int i=0;i<limit;i++) { int tmp=-dis[sx][sy][i]; for(int j=1;j<=d;j++) if(i&1<<(j-1))tmp+=w[j]; ans=max(ans,tmp); } } int main() { freopen("land.in","r",stdin); freopen("land.out","w",stdout); read(n,m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf(" %c",&mp[i][j]); if(isdigit(mp[i][j])) { pos[mp[i][j]-'0']=Node(i,j,0); d+=1; } if(mp[i][j]=='S') { sx=i; sy=j; mp[i][j]='.'; } } for(int i=1;i<=d;i++)w[i]=read<int>(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(mp[i][j]=='B') { pos[++d]=Node(i,j,0); w[d]=-INF; } limit=1<<d; SPFA(); printf("%d\n",ans); return 0; }
总结
还是很弱,但比前两天好一些了,继续加油。