4.8省选模拟
信心赛\(?\)
考场上期望\(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());
}