A - CF590E Birthday
题意
有个互不相同的字符串,你需要选出这些字符串的一个子集,使得这个子集内不存在两个不同的字符串,满足是的子串。问这个子集最多能包含多少个元素,并输出方案。,字符串的长度之和不超过。
Sol
是的子串等价于是的某一个前缀的后缀。由于后缀关系具有传递性(是的后缀,是的后缀可推出是的后缀),所以只需要求出所有的“是的某一个前缀的最长后缀”的关系,再跑一遍传递闭包就可以得到“所有的是的子串”的关系。
将字符串作为元素,定义偏序关系为:若为的子串则有(这个定义显然满足自反性、非对称性和传递性)。若并且我们就称和为不可比较的。题目要求我们求的就是一个最大的子集,满足集合中的元素两两不可比较,也就是求最长反链的大小。
由Dilworth’s theorem的证明过程我们可以得到这样的一个算法:对于一个偏序集,构造一个二分图,其中,而从的点到的点的边存在,当且仅当且。求出这张图的一个最大匹配,以及一个最小顶点覆盖,要求满足中的任意一个顶点都有邻边在中。令为在中对应的点和在中对应的点都不属于的点组成的集合,则为最长反链。
注意这道题的Trie的深度很大,在cf直接递归遍历会爆栈,必须使用非递归的实现方式。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
int G[755][755],n;
int atk[755],num;
namespace AC_Automation {
const int N=1e7+10;
const int M=1e8;
int ch[N][2];
int fail[N],ncnt=1,rt=1;
int id[N],par[N];
void insert(char *str,int len,int _id) {
int cur=rt;
for(int i=0;i<len;++i) {
int c=str[i]-'a';
if(!ch[cur][c]) ch[cur][c]=++ncnt;
cur=ch[cur][c];
}
if(id[cur]) num++,atk[_id]=1;
else id[cur]=_id;
}
int buc[755];
int stk[N],top;
int cur[N];
void dfs() {
stk[top=1]=rt;
while(top) {
int u=stk[top];
if(cur[u]==0) {
cur[u]=1;
if(id[par[u]]) buc[id[par[u]]]++;
if(id[u]) {
for(int j=1;j<=n;++j)
if(buc[j]) G[id[u]][j]=1;
buc[id[u]]++;
}
}
if(cur[u]==1) {
cur[u]=2;
if(ch[u][0]<M) {
stk[++top]=ch[u][0];
continue;
}
}
if(cur[u]==2) {
cur[u]=3;
if(ch[u][1]<M) {
stk[++top]=ch[u][1];
continue;
}
}
if(cur[u]==3) {
if(id[u]) buc[id[u]]--;
if(id[par[u]]) buc[id[par[u]]]--;
top--;
}
}
// if(id[par[u]]) buc[id[par[u]]]++;
// if(id[u]) {
// for(int j=1;j<=n;++j)
// if(buc[j]) G[id[u]][j]=1;
// buc[id[u]]++;
// }
//// if(id[u]) {
//// if(lastid) G[id[u]][lastid]=1;
//// if(id[par[u]]) G[id[u]][id[par[u]]]=1;
//// lastid=id[u];
//// }
// for(int i=0;i<2;++i) if(ch[u][i]<M) dfs(ch[u][i]);
// if(id[u]) buc[id[u]]--;
// if(id[par[u]]) buc[id[par[u]]]--;
}
queue<int> que;
void sol_str() {
for(int i=0;i<2;++i) if(ch[rt][i]) {
fail[ch[rt][i]]=par[ch[rt][i]]=rt;
que.push(ch[rt][i]);
}
else ch[rt][i]=rt+M;
while(!que.empty()) {
int u=que.front(); que.pop();
if(id[fail[u]]) par[u]=fail[u];
else par[u]=par[fail[u]];
for(int i=0;i<2;++i)
if(ch[u][i]) fail[ch[u][i]]=ch[fail[u]][i]%M,que.push(ch[u][i]);
else ch[u][i]=ch[fail[u]][i]%M+M;
}
dfs();
}
}
using AC_Automation::insert;
using AC_Automation::sol_str;
int used[755][755];
int To[755][2],inC[755][2];
int S,T,ncnt;
namespace Flow {
const int N=1550;
int head[N],dep[N],cur[N],ecnt;
struct ed { int to,next,f; };
vector<ed> e;
inline void init() { memset(head,-1,sizeof(head)); }
inline void ad(int x,int y,int f) {
e.PB((ed){y,head[x],f}); head[x]=e.size()-1;
e.PB((ed){x,head[y],0}); head[y]=e.size()-1;
}
queue<int> que;
bool bfs() {
for(int i=1;i<=ncnt;++i) dep[i]=-1,cur[i]=head[i];
while(!que.empty()) que.pop();
que.push(S),dep[S]=0;
while(!que.empty()) {
int u=que.front(); que.pop();
if(u==T) return 1;
for(int k=head[u];~k;k=e[k].next) if(e[k].f) {
int v=e[k].to; if(dep[v]!=-1) continue;
dep[v]=dep[u]+1,que.push(v);
}
}
return 0;
}
int dfs(int u,int f) {
if(u==T||!f) return f; int tmp,ret=0;
for(int &k=cur[u];~k;k=e[k].next) if(e[k].f) {
int v=e[k].to;
if(dep[v]==dep[u]+1&&(tmp=dfs(v,min(f,e[k].f)))) {
ret+=tmp,f-=tmp;
e[k].f-=tmp,e[k^1].f+=tmp;
if(!f) break;
}
}
return ret;
}
int work() { int ans=0; while(bfs()) ans+=dfs(S,1e9); return ans; }
void work_out() {
for(int i=n+1;i<=n+n;++i)
for(int k=head[i];~k;k=e[k].next) if(e[k].f) {
int v=e[k].to;
if(v<=n) {
used[v][i-n]=1;
To[v][0]=i-n;
To[i-n][1]=v;
}
}
}
}
char str[10000010];
int vis[755][2];
void dfs(int u,int p) {
if(vis[u][p]) return;
vis[u][p]=1;
for(int v=1;v<=n;++v) if((!p&&G[u][v])||(p&&G[v][u])) {
inC[v][p^1]=1;
dfs(To[v][p^1],p);
}
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
rd(n);
for(int i=1;i<=n;++i) scanf("%s",str),insert(str,strlen(str),i);
sol_str();
for(int i=1;i<=n;++i)
for(int k=1;k<=n;++k) if(G[i][k])
for(int j=1;j<=n;++j) G[i][j]|=G[k][j];
Flow::init();
S=n+n+1,T=n+n+2,ncnt=T;
for(int i=1;i<=n;++i) if(!atk[i])
for(int j=1;j<=n;++j) if(!atk[j]&&G[i][j])
Flow::ad(i,j+n,1);
for(int i=1;i<=n;++i) if(!atk[i]) Flow::ad(S,i,1),Flow::ad(i+n,T,1);
int ans=n-num-Flow::work();
Flow::work_out();
for(int i=1;i<=n;++i) if(!atk[i])
for(int d=0;d<2;++d)
if(!To[i][d]) dfs(i,d);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(To[i][0]==j&&!(inC[i][0]||inC[j][1]))
dfs(i,0);
printf("%d\n",ans);
for(int i=1;i<=n;++i) if(!atk[i]&&!(inC[i][0]|inC[i][1])) printf("%d ",i);
return 0;
}
B - AGC031D A Sequence of Permutations
Sol
令表示第个元素为的序列,令为满足的序列。则。
算出序列的前若干项:
观察发现序列中的每一项都可以写成的形式:
并且,而。观察:,由于,所以,也就是说,。
所以对于某一个,。某个置换进行次后得到的置换是可以算的,方法是找出每一个环(令环长为),每一个元素最终会置换到的元素是环上距离它为的元素。算出和直接暴力算出就可以了。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define PB push_back
#define MP make_pair
#define FIR first
#define SEC second
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=1e5+10;
int n;
void mul(int *a,int *p) {
static int b[N];
for(int i=1;i<=n;++i) b[i]=a[p[i]];
for(int i=1;i<=n;++i) a[i]=b[i];
}
int vis[N];
void Pow(int *a,int k) {
static int b[N],c[N],m;
for(int i=1;i<=n;++i) vis[i]=0;
for(int i=1;i<=n;++i) if(!vis[i]) {
m=0; int cur=i;
while(!vis[cur]) vis[b[m++]=cur]=1,cur=a[cur];
for(int i=0;i<m;++i) c[b[i]]=b[(i+k)%m];
}
for(int i=1;i<=n;++i) a[i]=c[i];
}
int p[N],q[N],rp[N],rq[N];
int a[N],ra[N],b[N],c[N];
int main() {
int L; rd(n),rd(L),L--;
for(int i=1;i<=n;++i) rd(p[i]),rp[p[i]]=i;
for(int i=1;i<=n;++i) rd(q[i]),rq[q[i]]=i;
for(int i=1;i<=n;++i) a[i]=i;
mul(a,q),mul(a,rp),mul(a,rq),mul(a,p);
Pow(a,L/6); L%=6;
for(int i=1;i<=n;++i) ra[a[i]]=i;
for(int i=1;i<=n;++i) b[i]=a[i];
mul(b,q),mul(b,ra);
mul(a,p),mul(a,ra);
while(L--) {
for(int i=1;i<=n;++i) ra[a[i]]=i,c[i]=b[i];
mul(c,ra);
for(int i=1;i<=n;++i) a[i]=b[i],b[i]=c[i];
}
for(int i=1;i<=n;++i) printf("%d ",a[i]);
return 0;
}
C - AGC026E Synchronized Subsequence
Sol
将原字符串划分成尽可能多的段,满足每一段和的数量相同。显然我们的操作对于每一段是独立的。考虑如何求出一段的最优答案:
- 如果这一段开头的字符是
a
,那么对于任意,第个a
都在第个b
之前出现。此时最优的答案一定是abababab……
。可以直接贪心求出最长的能选的abababab……
- 如果这一段开头的字符是
b
,那么对于任意,第个a
都在第个b
之后出现。假设最优策略中我们选了第个b
和第个a
,那么我们一定会选第个b
和第个a
,因为它们的位置关系一定形如,选上一定会更优。故而,我们的选择一定是最后的个a
和最后的个b
。直接枚举然后取最优的解,复杂度。
求出每一段的答案后,从后往前开始贪心:如果当前这一段大于等于后面的段得到的最优答案,就将这一段拼在后面的段的答案的前面,否则就扔掉这一段。总时间复杂度。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
vector<string> s;
string S,ans,cur;
int n,m;
void sol(string &s) {
vector<int> p1,p2;
string t="";
for(int i=0;i<s.length();++i)
if(s[i]=='a') p1.PB(i);
else p2.PB(i);
if(s[0]=='a') {
int last=-1;
for(int i=0;i<p1.size();++i)
if(p1[i]>last) last=p2[i],t+="ab";
}
else {
for(int i=0;i<p1.size();++i) {
string p="";
int x=i,y=i;
while(1)
if(x<p1.size()&&(y==p2.size()||p1[x]<p2[y]))
p+='a',x++;
else if(y<p2.size())
p+='b',y++;
else break;
if(p>t) t=p;
}
}
s=t;
}
int main() {
rd(n);
cin>>S;
int cnt=0;
for(int i=0;i<n*2;++i) {
if(S[i]=='a') cnt++;
else cnt--;
cur+=S[i];
if(cnt==0) {
s.PB(cur);
cur="";
}
}
for(int i=0;i<s.size();++i) sol(s[i]);
ans=s.back();
for(int i=(int)s.size()-2;i>=0;--i) {
int flg=1;
for(int j=0;j<min(s[i].length(),ans.length());++j)
if(s[i][j]!=ans[j]) {
if(s[i][j]<ans[j]) flg=0;
break;
}
if(flg) ans=s[i]+ans;
}
cout<<ans;
return 0;
}
来源:CSDN
作者:zhongyuwei20031224
链接:https://blog.csdn.net/weixin_45313881/article/details/103758404