T1 序列
写过的第一个迭代加深搜索,自然在考场上是想不出来的qwq
首先枚举答案,然后有一个核心函数:估价函数,用来估计离终点还有多远。
为了答案的准确性,估价函数\(g(x)\)和真正的距离函数\(f(x)\)需要满足\(g(x)\le f(x)\),同时为了速度,\(f(x)\)和\(g(x)\)需要尽量接近。
在这题里面,我们注意到每换一次最多只会改变一对相邻数之差的绝对值,所以可以设\(g(x)\)表示相邻数之差不为1的个数。
然后,我们开始搜索。当发现\(dep+g(x)>mxdep\)的时候就直接返回,顺便再加个不能连续翻转同一块的剪枝,于是就跑得飞快了。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template<typename T>
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
temp void read(T &x)
{
x=0;char ch=getchar(),t=0;
while (ch>'9'||ch<'0') t|=(ch=='-'),ch=getchar();
while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
if (t) x=-x;
}
void file()
{
#ifdef NTFAKIOI
freopen("a.in","r",stdin);
#endif
}
#define db double
#define ll long long
#define sz 25
int n;
vector<int>a,cur;
int mxdep;bool flg;
void dfs(int dep,int lst)
{
if (dep>mxdep||flg) return;
if (cur==a) return (void)(flg=1);
int cc=0;
rep(i,0,n-2) if (abs(cur[i+1]-cur[i])>1) ++cc;
if (dep+cc>mxdep) return;
rep(i,1,n-1)
if (i!=lst&&!flg)
reverse(cur.begin(),cur.begin()+i+1),dfs(dep+1,i),reverse(cur.begin(),cur.begin()+i+1);
}
void work()
{
read(n);a.resize(n);cur.resize(n);
rep(i,0,n-1) read(cur[i]),a[i]=i+1;
flg=0;
rep(i,0,1e9)
{
mxdep=i;
dfs(0,-1);
if (flg) return (void)(cout<<i<<'\n');
}
}
int main()
{
file();
int T;read(T);
while (T--) work();
return 0;
}
T2 轰炸
容易发现就是个最小反链覆盖,然而我把dilworth定理给忘了,而旁边的神仙个个都切了,于是就猜了个最长链的结论,顺便用数学归纳法伪证了一下,就过了。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template<typename T>
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
temp void read(T &x)
{
x=0;char ch=getchar(),t=0;
while (ch>'9'||ch<'0') t|=(ch=='-'),ch=getchar();
while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
if (t) x=-x;
}
void file()
{
#ifdef NTFAKIOI
freopen("a.in","r",stdin);
#endif
}
#define db double
#define ll long long
#define sz 1010101
#define v edge[i].t
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
int n,m;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
int deg[sz];
void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;++deg[t];}
int w[sz];
int dp[sz];
void solve()
{
queue<int>q;
rep(i,1,n) if (!deg[i]) q.push(i),dp[i]=w[i];
while (!q.empty())
{
int x=q.front();q.pop();
go(x)
{
chkmax(dp[v],dp[x]+w[v]);
if (!--deg[v]) q.push(v);
}
}
int ans=0;
rep(i,1,n) chkmax(ans,dp[i]);
cout<<ans;
}
namespace Build
{
int n,m;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;}
int dfn[sz],low[sz],cnt;
stack<int>st;
bool in[sz];
int bel[sz],T;
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;in[x]=1;st.push(x);
go(x)
{
if (!dfn[v]) tarjan(v),chkmin(low[x],low[v]);
else if (in[v]) chkmin(low[x],dfn[v]);
}
if (dfn[x]==low[x])
{
++T;int y;
do
{
y=st.top();st.pop();in[y]=0;
++w[T];bel[y]=T;
}while (y!=x);
}
}
void solve()
{
read(n),read(m);
int x,y;
rep(i,1,m) read(x),read(y),make_edge(x,y);
rep(i,1,n) if (!dfn[i]) tarjan(i);
rep(x,1,n) go(x) if (bel[x]!=bel[v]) ::make_edge(bel[x],bel[v]);
::n=T;
}
}
int main()
{
file();
Build::solve();
solve();
return 0;
}
T3 字符串
前几天刚口胡过类似的题,所以基本上看完题就会了。
就是分类讨论,有三种可能:在左边,在右边,被切成两半。
所以我们建出原串和翻转+反转之后的串的AC自动机,就可以设\(dp_{i,S,x}\)表示前\(i\)位,搞定了\(S\)串,AC自动机上跑到了\(x\),的方案数,然后随便DP。
统计答案的时候判一下左右接在一起能不能搞出其他串,就可以了。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template<typename T>
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
temp void read(T &x)
{
x=0;char ch=getchar(),t=0;
while (ch>'9'||ch<'0') t|=(ch=='-'),ch=getchar();
while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
if (t) x=-x;
}
void file()
{
#ifdef NTFAKIOI
freopen("a.in","r",stdin);
#endif
}
#define db double
#define ll long long
#define sz 1266
#define mod 998244353ll
int n;
char s[8][sz];
int m;
ll dp[2][70][sz];
int root=0,cnt;
int ch[sz][2],fail[sz],msk[sz],msk2[sz];
void insert(int id,char *s)
{
int n=strlen(s+1),x=root;
rep(i,1,n)
{
int &v=ch[x][s[i]-'0'];
if (!v) v=++cnt;
x=v;
}
msk[x]|=(1<<(id-1));
}
void getfail()
{
queue<int>q;
rep(i,0,1) if (ch[0][i]) q.push(ch[0][i]);
while (!q.empty())
{
int x=q.front();q.pop();
msk[x]|=msk[fail[x]],msk2[x]|=msk2[fail[x]];
rep(i,0,1)
{
int &v=ch[x][i];
if (v) fail[v]=ch[fail[x]][i],q.push(v);
else v=ch[fail[x]][i];
}
}
}
int main()
{
file();
read(n),read(m);
rep(i,1,n)
{
cin>>(s[i]+1);
insert(i,s[i]);
int l=strlen(s[i]+1);
rep(k,1,l) s[i][k]^=1;
reverse(s[i]+1,s[i]+l+1);
insert(i,s[i]);
rep(k,1,l) s[i][k]^=1;
reverse(s[i]+1,s[i]+l+1);
rep(k,1,l-1)
{
bool flg=1;
if (k<=l/2)
{
rep(t,1,k) flg&=(s[i][t]!=s[i][2*k-t+1]);
if (!flg) continue;
int x=root;
drep(t,l,k+1) x=ch[x][(s[i][t]-'0')^1];
msk2[x]|=(1<<(i-1));
}
else
{
rep(t,k+1,l) flg&=(s[i][t]!=s[i][2*k-t+1]);
if (!flg) continue;
int x=root;
rep(t,1,k) x=ch[x][s[i][t]-'0'];
msk2[x]|=(1<<(i-1));
}
}
}
getfail();
int cur=0,lst=1;
dp[0][0][0]=1;
int S=(1<<n)-1;
rep(i,0,m-1)
{
swap(cur,lst);
rep(s,0,S) rep(x,0,cnt) dp[cur][s][x]=0;
rep(s,0,S) rep(x,0,cnt) rep(t,0,1)
{
ll w=dp[lst][s][x];
(dp[cur][s|msk[ch[x][t]]][ch[x][t]]+=w)%=mod;
}
}
ll ans=0;
rep(s,0,S) rep(x,0,cnt) if ((s|msk2[x])==S) (ans+=dp[cur][s][x])%=mod;
cout<<ans;
return 0;
}