JOI Open 2016

链接

A. JOIRIS

神仙构造题。

首先考虑方块个数的限制。令 \(b_i\) 表示所有 \(j\mod k=i\)\(a_j\) 之和对 \(k\) 取模,合法当且仅当 \(b_0\sim b_{n\mod k-1}\) 全部相等,\(b_{n\mod k}\sim b_{n-1}\) 全部相等。

显然这是合法的必要条件,因为一次行操作会导致 \(b\) 所有位置 \(+1\),列操作不会改变 \(b\) 的值,每次消除会导致 \(b_0\sim b_{n\mod k-1}\) 减相同值,\(b_{n\mod k}\sim b_{n-1}\) 减相同值。

接下来用构造证明这也是充分的。

首先不断加竖条使 \(a\) 单调不降。接下来试图将 \(a_{k-1}\sim a_{n-1}\) 变成一样:从左往右不断叠横条,使得所有 \(>a_{k-1}\) 行的 \(k\sim n-1\) 列都有方块。接下来往 \(0\) 列放足够多方块,可以使左端点在 \(1\) 的行消掉,单后再往 \(1\) 列放,一直放到 \(k-1\) 列。这样所有 \(>a_{k-1}\) 的行都被消掉了。

由于之前的性质,此时 \(a_0\sim a_{n\mod k-1}\) 全部相等,\(b_{n\mod k}\sim b_{n-1}\) 全部为 \(0\)。那么不断往后面放横条把前面消掉即可。

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define N 110
using namespace std;
int a[N],b[N],n,k;
vector<pair<int,int>>ans;
void reset(){int v=*min_element(a,a+n);for(int i=0;i<n;i++) a[i]-=v;}
void push(int x){ans.emplace_back(1,x),a[x]+=k;}
void print(){for(int i=0;i<n;i++) cerr<<a[i]<<" ";cerr<<endl;}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++) scanf("%d",&a[i]),b[i%k]+=a[i];
    for(int i=0;i<k;i++) b[i]%=k;
    for(int i=0;i<n%k;i++) if(b[i]!=b[0]){puts("-1");return 0;}
    for(int i=n%k;i<k;i++) if(b[i]!=b[k-1]){puts("-1");return 0;}
    for(int i=1;i<n;i++) while(a[i]<a[i-1]) push(i);
    for(int i=0;i<k;i++) b[i]=0;
    reset();
    print();
    for(int i=k;i<n;i++)
    {
        for(int j=0;j<a[i]-a[i-1];j++)
            for(int p=i-k;p>=0;p-=k) ans.emplace_back(2,p);
        if(i%k) b[i%k-1]+=a[i]-a[i-1];
    }
    for(int i=k-1;i>=0;i--) b[i]+=b[i+1];
    for(int i=0;i<k-1;i++)
        while(a[i]<a[k-1]+b[i]) push(i);
    for(int i=0;i<k-1;i++) a[i]-=b[i];
    for(int i=k;i<n;i++) a[i]=a[i-1];
    reset();
    print();
    for(int i=0;i<a[0];i++)
        for(int j=n%k;j<n;j+=k) ans.emplace_back(2,j);
    printf("%d\n",(int)ans.size());
    for(auto v:ans) printf("%d %d\n",v.first,v.second+1);
    return 0;
}

B. Selling RNA Strands

用 Trie 树处理前缀关系,将所有后缀用另一个 Trie 树/Hash 离散化后线段树处理。复杂度 \(O(n\log n)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=4000010,M=N*22;
char s[N];
struct node{int x,y;node(int X=0):x(X),y(X){}node(int X,int Y):x(X),y(Y){}};
bool operator ==(const node a,const node b){return a.x==b.x && a.y==b.y;}
bool operator <(const node a,const node b){return a.x==b.x?a.y<b.y:a.x<b.x;}
node bs[N],h[N];
const int mo1=1000000009,mo2=1019260817,base=2333;
int add(int x,int y,int mod){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y,int mod){return x-y<0?x-y+mod:x-y;}
node operator +(node a,node b){return node(add(a.x,b.x,mo1),add(a.y,b.y,mo2));}
node operator -(node a,node b){return node(dec(a.x,b.x,mo1),dec(a.y,b.y,mo2));}
node operator *(node a,node b){return node(1ll*a.x*b.x%mo1,1ll*a.y*b.y%mo2);}
ll to_ll(node a){return 1ll*a.x*mo2+a.y;}
void init(int n=N-10){bs[0]=1;for(int i=1;i<=n;i++) bs[i]=bs[i-1]*base;}
ll get_hs(int m){h[0]=0;for(int i=1;i<=m;i++) h[i]=h[i-1]*base+(s[i]-'A'+1);return to_ll(h[m]);}
int id(char c){return c=='A'?0:(c=='G'?1:(c=='C'?2:3));}
ll tmp[N];int tt;
vector<ll>g[N];
int pos[N],ch[N][4],cnt=1;
int insert(int m)
{
    int u=1;
    for(int i=m;i;u=ch[u][id(s[i])],i--)
        if(!ch[u][id(s[i])]) ch[u][id(s[i])]=++cnt;
    return u;
}
int dfn[N],siz[N],idx;
void dfs(int u)
{
    dfn[u]=++idx,siz[u]=1;
    for(int i=0;i<4;i++) if(ch[u][i]) dfs(ch[u][i]),siz[u]+=siz[ch[u][i]];
}
int t[M],ls[M],rs[M],tid;
void insert(int &u,int l,int r,int p,int v)
{
    if(!u) u=++tid;t[u]+=v;if(l==r) return;
    int mid=(l+r)>>1;
    if(p<=mid) insert(ls[u],l,mid,p,v);else insert(rs[u],mid+1,r,p,v);
}
int qry(int u,int l,int r,int L,int R)
{
    if(!u) return 0;
    if(L<=l && r<=R) return t[u];
    int mid=(l+r)>>1,v=0;
    if(L<=mid) v+=qry(ls[u],l,mid,L,R);
    if(R>mid) v+=qry(rs[u],mid+1,r,L,R);
    return v;
}
int rt[N];
int main()
{
    init();
    int n,q;scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        int m=strlen(s+1);
        get_hs(m);
        g[i].resize(m+1);
        for(int j=0;j<=m;j++) tmp[++tt]=g[i][j]=to_ll(h[j]);
        pos[i]=insert(m);
    }
    dfs(1);
    sort(tmp+1,tmp+tt+1);
    tt=unique(tmp+1,tmp+tt+1)-tmp-1;
    for(int i=1;i<=n;i++)
        for(ll v:g[i])
        {
            int p=lower_bound(tmp+1,tmp+tt+1,v)-tmp;
            insert(rt[p],1,idx,dfn[pos[i]],1);
        }
    for(int i=1;i<=q;i++)
    {
        scanf("%s",s+1);int m=strlen(s+1);
        ll v=get_hs(m);int p=lower_bound(tmp+1,tmp+tt+1,v)-tmp;
        scanf("%s",s+1);m=strlen(s+1);
        int u=1;
        for(int i=m;i && u;u=ch[u][id(s[i])],i--);
        if(tmp[p]!=v){puts("0");continue;}
        if(!u){puts("0");continue;}
        printf("%d\n",qry(rt[p],1,idx,dfn[u],dfn[u]+siz[u]-1));
    }
    return 0;
}

C. Skyscraper

经典折线 dp。

直接从左往右不好处理,考虑从下往上,即从小到大插入每个值。

考虑将最后的序列看做折线,那么 dp 过程中会出现若干线头。对于每一个区域,所有没有结束的线头都必须穿过该区域产生代价。设 \(f_{i,j,k,0/1/2}\) 表示枚举到第 \(i\) 个,线段有 \(j\) 条,当前和为 \(k\),选了多少个端点。那么新增的点可以合并两个线头,新增一条线段。对端点处特判。

复杂度 \(O(n^2m)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110,M=1010,mod=1000000007;
int a[N],f[N][M][3],g[N][M][3];
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    if(n==1){puts("1");return 0;}
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    f[0][0][0]=1;
    for(int i=1;i<n;i++)
    {
        memcpy(g,f,sizeof(g)),memset(f,0,sizeof(f));
        for(int j=0;j<i;j++)
            for(int k=0;k<=m;k++)
                for(int p=0;p<=2;p++) if(g[j][k][p])
                {
                    int u=g[j][k][p],v=a[i+1]-a[i],w=j*2+p;
                    if(k+v*(w+2)<=m) add(f[j+1][k+v*(w+2)][p],u);
                    if(k+v*w<=m) add(f[j][k+v*w][p],1ll*u*w%mod);
                    if(j && k+v*(w-2)<=m) add(f[j-1][k+v*(w-2)][p],1ll*u*(j*(j-1)+p*j)%mod);
                    if(p<2)
                    {
                        if(j && k+v*(w-1)<=m) add(f[j-1][k+v*(w-1)][p+1],1ll*u*j*(2-p)%mod);
                        if(k+v*(w+1)<=m) add(f[j][k+v*(w+1)][p+1],1ll*u*(2-p)%mod);
                    }
                }
    }
    int ans=0;
    for(int i=0;i<=m;i++) add(ans,f[0][i][2]),add(ans,f[0][i][1]);
    printf("%d\n",ans);
    return 0;
}
posted @ 2022-04-10 19:15  Flying2018  阅读(151)  评论(0)    收藏  举报