2019-2020 ACM-ICPC Latin American Regional Programming Contest 训练记录
solved:7/13
upsolved:7/13
A.
把每种算法集合看成点,然后一个点向其删掉一个的子集连边,然后问题就变成了在一个DAG中选一些没有先后关系的点
这个问题是最长反链,然后最长反链变成了最小链覆盖
DAG上最小链覆盖可以拆点建二部图,答案是\(n-\)最大匹配
1 #include<bits/stdc++.h> 2 #define ull unsigned long long 3 using namespace std; 4 int N; 5 map<string,int> id; 6 map<ull,int> mp; 7 int n,cnt; 8 namespace Maxflow 9 { 10 const int inf = 1000000007; 11 int head[220005],p; 12 struct edge{int to,next,f;}e[6000005]; 13 void init() 14 { 15 memset(head,-1,sizeof(head)); 16 memset(e,0,sizeof(e)); 17 p=0; 18 } 19 void addedge(int u,int v,int f) 20 { 21 e[p].to=v;e[p].f=f;e[p].next=head[u];head[u]=p++; 22 e[p].to=u;e[p].f=0;e[p].next=head[v];head[v]=p++; 23 } 24 int dis[220005]; 25 bool bfs(int s,int t) 26 { 27 memset(dis,0,sizeof(dis)); 28 queue<int> q;q.push(s);dis[s]=1; 29 while(!q.empty()) 30 { 31 int u=q.front();q.pop(); 32 for(int i=head[u];i!=-1;i=e[i].next) 33 { 34 int v=e[i].to; 35 int f=e[i].f; 36 if(f&&!dis[v])dis[v]=dis[u]+1,q.push(v); 37 } 38 } 39 return dis[t]!=0; 40 } 41 int dfs(int u,int maxf,int t) 42 { 43 if(u==t)return maxf; 44 int tmp=0; 45 for(int i=head[u];i!=-1&&tmp<maxf;i=e[i].next) 46 { 47 int v=e[i].to; 48 int f=e[i].f; 49 if(f&&dis[v]==dis[u]+1) 50 { 51 int minn=min(maxf-tmp,f); 52 f=dfs(v,minn,t); 53 tmp+=f; 54 e[i].f-=f;e[i^1].f+=f; 55 } 56 } 57 if(!tmp)dis[u]=inf; 58 return tmp; 59 } 60 int dinic(int s,int t) 61 { 62 int ans=0; 63 while(bfs(s,t))ans+=dfs(s,inf,t); 64 return ans; 65 } 66 } 67 int main() 68 { 69 scanf("%d",&N); 70 Maxflow::init(); 71 for(int i=1;i<=N;++i) 72 { 73 int A; 74 scanf("%d",&A); 75 vector<int> tmp[1030]; 76 int a[12]; 77 ull xs[1030]; 78 char str[12]; 79 memset(a,0,sizeof(a)); 80 for(int i=0;i<A;++i) 81 { 82 scanf("%s",str); 83 if(!id.count((string)str))id[(string)str]=++cnt; 84 a[i]=id[str]; 85 } 86 //for(int i=0;i<A;++i)cerr<<a[i]<<" "; 87 //cerr<<endl; 88 for(int S=1;S<(1<<A);++S) 89 { 90 tmp[S].clear(); 91 for(int j=0;j<A;++j)if(S&(1<<j))tmp[S].push_back(a[j]); 92 sort(tmp[S].begin(),tmp[S].end()); 93 ull y=0; 94 for(int x:tmp[S])y=y*10007+x; 95 xs[S]=y; 96 //cerr<<S<<" "<<y<<endl; 97 if(!mp.count(y))mp[y]=++n; 98 //cerr<<n<<endl; 99 } 100 for(int S=1;S<(1<<A);++S) 101 { 102 for(int i=0;i<A;++i)if(S&(1<<i)) 103 { 104 int s=S^(1<<i); 105 Maxflow::addedge(mp[xs[S]],mp[xs[s]]+110000,1); 106 } 107 } 108 } 109 for(int i=1;i<=n;++i)Maxflow::addedge(0,i,1),Maxflow::addedge(i+110000,220001,1); 110 int ans=n-Maxflow::dinic(0,220001); 111 printf("%d\n",ans); 112 }
E.
随便双指针扫一下就行了
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 char s[200005]; 5 int n,k; 6 int c1,c2; 7 int main() 8 { 9 scanf("%s",s+1); 10 n=strlen(s+1); 11 for(int i=1;i<=n;++i)s[i+n]=s[i]; 12 scanf("%d",&k); 13 int r1=0,c1=0; 14 ll ans=0; 15 for(int l=1;l<=n;++l) 16 { 17 while(r1<l+n-1&&(!c1)) 18 { 19 if(s[r1+1]=='E')c1++; 20 r1++; 21 } 22 ans+=max(l+k-r1,0); 23 if(s[l]=='E')c1--; 24 } 25 printf("%lld\n",ans); 26 }
G.
考虑每次贪心地匹配就是对的
我们把所有串连在一起用不重复的东西隔开做一个后缀数组
我们预处理SA上朝左朝右能碰到的第一个位置,该位置属于第一个串
对于询问串的每一个位置,其和第一个串的LCP就在之前处理的两个位置里
然后我们从每个串的开头位置开始,查询LCP,然后贪心向后跳
1 #include<bits/stdc++.h> 2 #define maxn 800005 3 using namespace std; 4 struct Suffix_Array 5 { 6 int n,s[maxn],x[maxn],y[maxn],c[maxn],sa[maxn],rk[maxn],h[maxn]; 7 void build(int m) 8 { 9 for(int i=1;i<=m;i++)c[i]=0; 10 for(int i=1;i<=n;i++)c[x[i]=s[i]]++; 11 for(int i=1;i<=m;i++)c[i]+=c[i-1]; 12 for(int i=n;i>=1;i--)sa[c[x[i]]--]=i; 13 for(int k=1;k<=n;k<<=1) 14 { 15 int p=0; 16 for(int i=n-k+1;i<=n;i++)y[++p]=i; 17 for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; 18 for(int i=1;i<=m;i++)c[i]=0; 19 for(int i=1;i<=n;i++)c[x[y[i]]]++; 20 for(int i=1;i<=m;i++)c[i]+=c[i-1]; 21 for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; 22 swap(x,y);x[sa[1]]=p=1; 23 for(int i=2;i<=n;i++) 24 if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])x[sa[i]]=p;else x[sa[i]]=++p; 25 if(p>=n)break; 26 m=p+1; 27 } 28 for(int i=1;i<=n;i++)rk[sa[i]]=i; 29 int k=0; 30 for(int i=1;i<=n;i++) 31 { 32 int j=sa[rk[i]-1]; 33 if(k)k--;while(s[i+k]==s[j+k])k++; 34 h[rk[i]]=k; 35 } 36 } 37 int st[maxn][22],Log[maxn]; 38 void init() 39 { 40 for(int i=1;i<=n;i++)st[i][0]=h[i]; 41 for(int i=1;i<=n;++i)Log[i]=log2(i); 42 for(int j=1;(1<<j)<=n;j++) 43 for(int i=1;i+(1<<j)-1<=n;i++) 44 st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); 45 } 46 int lcp(int x,int y) 47 { 48 if(x==y)return n-x+1; 49 int l=rk[x],r=rk[y]; 50 if(l>r)swap(l,r);l++; 51 int k=Log[r-l+1]; 52 return min(st[l][k],st[r-(1<<k)+1][k]); 53 } 54 }sa; 55 int n,m,st[maxn],len[maxn]; 56 char tmp[maxn]; 57 int s[maxn]; 58 int pre[maxn],suf[maxn]; 59 int main() 60 { 61 scanf("%s",tmp+1); 62 st[0]=1;len[0]=strlen(tmp+1); 63 for(int i=1;i<=len[0];++i)s[++m]=tmp[i]-'A'+1; 64 scanf("%d",&n); 65 for(int i=1;i<=n;++i) 66 { 67 s[++m]=199999+i; 68 scanf("%s",tmp+1); 69 st[i]=m+1;len[i]=strlen(tmp+1); 70 for(int j=1;j<=len[i];++j)s[++m]=tmp[j]-'A'+1; 71 } 72 for(int i=1;i<=m;++i)sa.s[++sa.n]=s[i]; 73 sa.build(400000); 74 sa.init(); 75 for(int i=1;i<=m;++i) 76 { 77 if(sa.sa[i]<=len[0])pre[i]=i; 78 else pre[i]=pre[i-1]; 79 } 80 for(int i=m;i>=1;--i) 81 { 82 if(sa.sa[i]<=len[0])suf[i]=i; 83 else suf[i]=suf[i+1]; 84 } 85 for(int i=1;i<=n;++i) 86 { 87 int pos=st[i],ans=0,yes=1; 88 while(pos<=st[i]+len[i]-1) 89 { 90 int p=sa.rk[pos]; 91 int x=pre[p],y=suf[p]; 92 int L=0; 93 if(x)L=max(L,sa.lcp(sa.sa[p],sa.sa[x])); 94 if(y)L=max(L,sa.lcp(sa.sa[p],sa.sa[y])); 95 if(!L) 96 { 97 puts("-1");yes=0; 98 break; 99 } 100 else ans++,pos+=L; 101 } 102 if(yes)printf("%d\n",ans); 103 } 104 }
I.
考虑先跑一遍能跑到多少个结束点,然后把能跑到的路径拿出来做一遍拓扑排序DP
1 #include<bits/stdc++.h> 2 #define maxn 2005 3 using namespace std; 4 const int mod = 1000000007; 5 int n,m; 6 vector<int> g[maxn],g2[maxn]; 7 int vis[maxn],dp[maxn],d[maxn]; 8 int main() 9 { 10 scanf("%d%d",&n,&m); 11 for(int i=1;i<=m;++i) 12 { 13 int k; 14 scanf("%d",&k); 15 while(k--) 16 { 17 int v; 18 scanf("%d",&v); 19 g[i].push_back(v); 20 } 21 } 22 queue<int> q; 23 vis[1]=1;q.push(1); 24 while(!q.empty()) 25 { 26 int u=q.front();q.pop(); 27 for(int v:g[u]) 28 { 29 if(!vis[v])vis[v]=1,q.push(v); 30 } 31 } 32 for(int u=1;u<=n;++u)if(vis[u]) 33 for(int v:g[u])if(vis[v])g2[u].push_back(v),d[v]++; 34 dp[1]=1;q.push(1); 35 while(!q.empty()) 36 { 37 int u=q.front();q.pop(); 38 for(int v:g[u]) 39 { 40 dp[v]=(dp[v]+dp[u])%mod; 41 d[v]--; 42 if(!d[v])q.push(v); 43 } 44 } 45 int ans1=0,ans2=0; 46 for(int i=m+1;i<=n;++i)ans1=(ans1+dp[i])%mod,ans2+=vis[i]; 47 printf("%d %d\n",ans1,ans2); 48 }
K.
用零点存在定理构造一下
然后考虑根两两不同,系数又不超过\(2^{63}-1\),因此项数是\(log\)级别的,暴力多项式乘法就完事了
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define maxn 10005 4 using namespace std; 5 int n; 6 char s[maxn]; 7 vector<ll> mul(vector<ll> A,vector<ll> B) 8 { 9 int n=A.size(),m=B.size(); 10 vector<ll> C;C.clear();C.resize(n+m-1); 11 for(int i=0;i<n;++i) 12 for(int j=0;j<m;++j)C[i+j]+=A[i]*B[j]; 13 return C; 14 } 15 int main() 16 { 17 scanf("%s",s+1); 18 n=strlen(s+1); 19 vector<ll> Ans; 20 Ans.clear(); 21 Ans.push_back(1); 22 for(int i=2;i<=n;++i)if(s[i]!=s[i-1]) 23 { 24 vector<ll> tmp; 25 tmp.clear(); 26 tmp.push_back(-(2*i-1)); 27 tmp.push_back(1); 28 Ans=mul(Ans,tmp); 29 } 30 if(s[n]=='A')for(int i=0;i<Ans.size();++i)Ans[i]=-Ans[i]; 31 printf("%d\n",Ans.size()-1); 32 for(int i=Ans.size()-1;i>=0;--i)printf("%I64d ",Ans[i]); 33 }
L.
处理出每个点往右相同颜色的最大长度记为\(r[i][j]\),对每一列维护单调栈求得每个点作为该列r最小值的长度\(l[i][j]\),每个点则可对应一个边长为\(min(r[i][j],l[i][j])\)的正方形更新答案。
#include<bits/stdc++.h> using namespace std; const int N=1e3+10; int n,m,up[N][N],rt[N][N],lf[N][N],ans,l[N],r[N],s[N]; int st[N],top; char a[N][N]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",a[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)rt[i][j]=j; for(int i=1;i<=n;i++) for(int j=m-1;j>0;j--) if(a[i][j]==a[i][j+1]) rt[i][j]=rt[i][j+1]; for(int i=1;i<=m;i++){ top=0; for(int j=1;j<=n;j++){ s[j]=rt[j][i]-i+1; while(top&&s[st[top]]>=s[j])top--; if(top==0)l[j]=1; else l[j]=st[top]+1; st[++top]=j; } top=0; for(int j=n;j;j--){ while(top&&s[st[top]]>s[j])top--; if(top==0)r[j]=n; else r[j]=st[top]-1; st[++top]=j; } for(int j=1;j<=n;j++){ int xx=min(s[j],r[j]-l[j]+1); ans=max(ans,xx*xx); } } printf("%d\n",ans); }
M.
直接维护所选子段的左右端点扫一遍。
#include<bits/stdc++.h> using namespace std; const int N=1e3+10; int a[N],n,k,b[N],ans=1; int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int l=1,r=1; while(r<=n){ while(a[r+1]-a[r]<=k&&r<n)r++; ans=max(ans,r-l+1); l=r=r+1; if(r>n)break; } printf("%d\n",ans); }

浙公网安备 33010602011771号