牛客练习赛 57——manacher算法 树形dp?

    科技2022-07-10  94

    A - Tic-Tac-Toe

    直接考虑每个人8种赢的情况即可。

    #define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<iostream> #include<algorithm> using namespace std; const int N=5; char g[N][N]; int main() { //IO; int T=1; cin>>T; while(T--) { for(int i=1;i<=3;i++) cin>>g[i]+1; bool ok1=0,ok2=0; if(g[1][1]=='A'&&g[1][2]=='A'&&g[1][3]=='A') ok1=1; if(g[2][1]=='A'&&g[2][2]=='A'&&g[2][3]=='A') ok1=1; if(g[3][1]=='A'&&g[3][2]=='A'&&g[3][3]=='A') ok1=1; if(g[1][1]=='A'&&g[2][1]=='A'&&g[3][1]=='A') ok1=1; if(g[1][2]=='A'&&g[2][2]=='A'&&g[3][2]=='A') ok1=1; if(g[1][3]=='A'&&g[2][3]=='A'&&g[3][3]=='A') ok1=1; if(g[1][1]=='A'&&g[2][2]=='A'&&g[3][3]=='A') ok1=1; if(g[1][3]=='A'&&g[2][2]=='A'&&g[3][1]=='A') ok1=1; if(g[1][1]=='B'&&g[1][2]=='B'&&g[1][3]=='B') ok2=1; if(g[2][1]=='B'&&g[2][2]=='B'&&g[2][3]=='B') ok2=1; if(g[3][1]=='B'&&g[3][2]=='B'&&g[3][3]=='B') ok2=1; if(g[1][1]=='B'&&g[2][1]=='B'&&g[3][1]=='B') ok2=1; if(g[1][2]=='B'&&g[2][2]=='B'&&g[3][2]=='B') ok2=1; if(g[1][3]=='B'&&g[2][3]=='B'&&g[3][3]=='B') ok2=1; if(g[1][1]=='B'&&g[2][2]=='B'&&g[3][3]=='B') ok2=1; if(g[1][3]=='B'&&g[2][2]=='B'&&g[3][1]=='B') ok2=1; if(ok1&&ok2) cout<<"invalid\n"; else if(ok1) cout<<"Yes\n"; else if(ok2) cout<<"No\n"; else cout<<"draw\n"; } return 0; }

    B - 打 boss

    如果第一次都能打死就不考虑回复血的情况 如果第一次打不死并且回血不少于掉血肯定永远打不死 人死之前最多能攻击 ⌈ h 1 a 2 ⌉ \lceil\frac{h_1}{a_2} \rceil a2h1次,如果死之前打不死(需要干掉 h 2 + m ⌈ h 1 a 2 − 1 ⌉ h2+m\lceil\frac{h_1}{a_2}-1 \rceil h2+ma2h11血量)那么就打不死,否则能打死。

    #define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<set> #include<map> #include<cmath> #include<queue> #include<string> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<unordered_map> using namespace std; typedef long long ll; typedef pair<int,int> pii; int main() { IO; int T=1; cin>>T; while(T--) { ll h1,h2; ll a1,a2; ll m; cin>>h1>>h2>>a1>>a2>>m; if(h2<=a1) cout<<"Yes\n"; else if(m>=a1) cout<<"No\n"; else if(((h1+a2-1)/a2)*a1<h2+m*((h1+a2-1)/a2-1)) cout<<"No\n"; else cout<<"Yes\n"; } return 0; }

    C - 装货物

    搜索渣渣。没经历过偏分的oi没能成为搜索king

    #define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=30; ll w[N]; int n,m,W; ll now[N]; bool ok; void dfs(int u,int cnt) { if(ok) return;//这个优化直接让我过了 if(u>n) { ok=1; return; } for(int i=1;i<=cnt;i++) { if(now[i]+w[u]<=W) { now[i]+=w[u]; dfs(u+1,cnt); now[i]-=w[u]; } } if(w[u]<=W&&cnt+1<=m) { now[cnt+1]=w[u]; dfs(u+1,cnt+1); now[cnt+1]-=w[u]; } } int main() { IO; int T=1; cin>>T; while(T--) { cin>>n>>m>>W; for(int i=1;i<=n;i++) cin>>w[i]; sort(w+1,w+1+n); reverse(w+1,w+1+n); ok=0; memset(now,0,sizeof now); dfs(1,0); if(ok) cout<<"Yes\n"; else cout<<"No\n"; } return 0; }

    D - 回文串

    学了一下manacher算法,模板++ 用manacher算法求后p[i]-1即表示原字符串回文序列长度,而且经过对字符串进行添加*操作后最终每个回文字符串的最左或右端一定是*。 对于本题需求不相交且长度和最大的非空回文子串,我们可以尝试枚举划分点,左边最长的回文子串+右边最长的回文子串(预处理前缀后缀即可)即可求出。

    注意:由于每个回文字符串的最左或右端一定是*,一定不会出现相交的情况。 非空想想不难知道只有在整个字符串都是回文串的情况下非空才会和不非空的结果不一样(其他情况非空的结果一定更优)。我们需要特判整个字符串是回文串的情况。

    #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=400010; int n; char s[N]; int p[N],lmax[N],rmax[N]; int init() { n=strlen(s+1); for(int i=2*n;i;i-=2) { s[i]=s[i/2]; s[i-1]='*'; } s[2*n+1]='*'; s[0]='@',s[2*n+2]='&';//哨兵 return 2*n+1; } int manacher() { int res=0; int now=0,mx=0; for(int i=1;i<=n;i++) { p[i]=i<mx?min(p[2*now-i],mx-i):1; while(s[i+p[i]]==s[i-p[i]]) p[i]++; if(i+p[i]>mx) { mx=i+p[i]; now=i; } res=max(res,p[i]-1); } return res; } int main() { cin>>s+1; n=init(); manacher(); for(int i=1;i<=n;i++) { int r=p[i]+i-1; rmax[r]=max(rmax[r],p[i]-1); } for(int i=n;i;i--) { int l=i-p[i]+1; lmax[l]=max(lmax[l],p[i]-1); } for(int i=1;i<=n;i++) rmax[i]=max(rmax[i-1],rmax[i]); for(int i=n;i>=1;i--) lmax[i]=max(lmax[i+1],lmax[i]); int res=0; int ok=0; // 特判整个字符串都是回文串 for(int i=1;i<=n;i++) if(p[i]-1==(n-1)/2) ok=1; if(ok) cout<<(n-1)/2-1<<'\n'; else { for(int i=1;i<=n;i++) res=max(res,lmax[i]+rmax[i]); cout<<res<<'\n'; } return 0; }

    E - 划分树

    大佬题解

    #define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N=500010; const ll mod=1004535809; int h[N],e[2*N],ne[2*N],idx; int n,m; ll w[N]; ll f[N][2]; void add(int a,int b) { e[idx]=b; ne[idx]=h[a]; h[a]=idx++; } void dfs(int u) { f[u][0]=1;//删除偶数条边(不删)方案数是1 ll cnt1,cnt0; for(int i=h[u];i!=-1;i=ne[i]) { int j=e[i]; dfs(j); w[u]^=w[j]; cnt0=f[u][0],cnt1=f[u][1]; f[u][0]=(cnt0*f[j][0]+cnt1*f[j][1])%mod; f[u][1]=(cnt1*f[j][0]+cnt0*f[j][1])%mod; } cnt0=f[u][0],cnt1=f[u][1]; if(u==1) return; // 考虑连向父亲 if(w[u]==0) f[u][0]=(f[u][0]+cnt1)%mod; if(w[u]==m) f[u][1]=(f[u][1]+cnt0)%mod; } int main() { IO; int T=1; //cin>>T; while(T--) { cin>>n>>m; memset(h,-1,sizeof h); for(int i=2;i<=n;i++) { int p; cin>>p; add(p,i); } for(int i=1;i<=n;i++) cin>>w[i]; dfs(1); cout<<(f[1][0]*(w[1]==m)+f[1][1]*(w[1]==0))%mod<<'\n'; } return 0; }

    F有点懵,先记下来。 要加油哦~

    Processed: 0.020, SQL: 8