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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

 

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);
}
View Code

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);
} 
View Code

 

posted @ 2020-03-13 21:28  uuzhatetree  阅读(358)  评论(0)    收藏  举报