2019杭電多校(第二場)


1005 Everything Is Generatoed In Equal Probability(数学)
http://acm.hdu.edu.cn/showproblem.php?pid=6595
題意
期待する
Nを一つあげます 問1−Nでランダムに1つの数を選択し、ランダムに1−nを並べて逆の順を求める.その後、サブシーケンスをランダムに選択して逆の順を求める.
考え方
n個の数がそろっている逆順は、希望個数に対して(n*(n-1)/4  全部でn*(n-1)/2が逆順で期待されるのは1/2です. 
dp[i]は配列長iの後継期待である. 
dp[i]=(n*(n-1)/4+dp[i]*C[0]/pow(2,n)+ Σdp[i]*C[i]、[n]/pow(2,n)
dp[i]=(n(n-1)/4+ Σdp[i]*C[i]/pow(2,n)*pow(2,n)/(pow(2,n)-1
Nに対してdp 1−nの和を計算し、Nで割る.
コード
#include

using namespace std;

typedef long long ll;
const ll mod = 998244353;
ll C[3300][3300];
ll dp[3300];
ll q[3300];
ll qw[3300];
ll qw2[3300];
ll qsort(ll x,ll n)
{
    ll ans = 1;
    while(n)
    {
        if(n % 2 == 1)
        {
            ans = (ans * x) % mod;
        }
        x = (x * x) % mod;
        n /= 2;
    }
    ans %= mod;
    return ans;
}

void ok()
{
    ll xx = 1;
    for(ll i = 1;i <= 3000;i++)
    {
        xx *= 2;
        xx %= mod;
        q[i] = xx;
        qw[i] = qsort(xx,mod-2);
        qw2[i] = qsort(xx-1,mod-2);
    }
    dp[0] = 0;
    dp[1] = 0;
    ll qwe = qsort(4,mod-2);
    for(ll i = 2;i <= 3000;i++)
    {
        dp[i] = (i*(i-1)*qwe)%mod;
        for(ll j = 1;j < i;j++)
        {
            dp[i] = (dp[i] + ((C[i][j]*dp[j])%mod)*qw[i])%mod;
        }
        dp[i] = ((dp[i]*q[i])%mod*(qw2[i]))%mod;
    }
}
 void cc(ll n)
 {
     for(ll i=0;i<=n;i++)
       C[i][0]=1;
     for(ll i=1;i<=n;i++)
     {
         for(ll j=1;j<=i;j++)
         C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
     }
 }
int main()
{
    ll n;
    cc(3002);
    ok();
    while(scanf("%lld",&n)!= EOF)
    {
        ll ans = 0;
        for(ll i = 2;i <= n;i++)
        {
            ans = (ans + dp[i]) % mod;
        }
        ans = (ans * qsort(n,mod-2))%mod;
        printf("%lld
",ans); } return 0; }
1009 I Love Palindrome String(回文樹)
http://acm.hdu.edu.cn/showproblem.php?pid=6599
題意
リターンツリー
長さnの文字列をあげます.
自分のために回文串の前半も回文串の数です.
考え方
前半は回文だから、後半も回文列に違いない.
回文樹のfailは現在の最後の最長の回文列を指します.どのように回文列のfailポインタが指す回文列の長さですか?それとも前の長さを続けて彼の半分が存在します.彼は要求に合う回文列です.
再帰的に長さの半分を探すとタイムアウトします.failポインタの方向に沿ってDFSを走る木を作ってもいいです.
コード
#include 

using namespace std;

const int MAXN = 310005 ;
const int N = 26 ;

struct Palindromic_Tree {
    int next[MAXN][N] ;//next  ,next        ,                   
    int fail[MAXN] ;//fail  ,      fail       
    long long cnt[MAXN] ;
    //cnt[i]    i            (           ,  count()            )
    int num[MAXN] ; //num[i]     i                         
    int len[MAXN] ;//len[i]    i         
    int S[MAXN] ;//       
    int last ;//            ,     add
    int n ;//      
    int p ;//    

    vector to[MAXN]; //       

    int newnode ( int l ) {//    
        for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
        cnt[p] = 0 ;
        num[p] = 0 ;
        len[p] = l ;
        return p ++ ;
    }

    void init () {//   
        p = 0 ;
        newnode (0) ;
        newnode (-1) ;
        last = 0 ;
        n = 0 ;
        S[n] = -1 ;//              ,    
        fail[0] = 1 ;
        to[1].push_back(0);
    }

    int get_fail ( int x ) {// KMP  ,           
        while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
        return x ;
    }

    void add ( int c ) {
        c -= 'a';
        S[++ n] = c ;
    //  cout<= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
        //       cnt,    fail[v]=u, u   v     !
    }
} ;
Palindromic_Tree A;
char s[MAXN];
int vis[MAXN],ans[MAXN];
void dfs(int u)
{
    if(vis[(A.len[u]+1) / 2] == 1) ans[A.len[u]] += A.cnt[u];
    if(A.len[u] >= 0) vis[A.len[u]] = 1;
    for(int i = 0;i < A.to[u].size();i++)
    {
        int v = A.to[u][i];
        dfs(v);
    }
    if(A.len[u] >= 0) vis[A.len[u]] = 0;
}
int main()
{
    while(scanf("%s",s ) != EOF)
    {
        int n = strlen(s);
        for(int i = 0;i <= n;i++)
        {
            A.to[i].clear();
        }
        memset(ans,0,sizeof(ans));
        A.init();
        for(int i =0;i < n;i++)
        {
            A.add(s[i]);
        }
        A.count();
        dfs(1);
        ans[1] = n;
        for(int i = 1;i <= n;i++)
        {
            if(i!= 1) printf(" ");
            printf("%d",ans[i]);
        }
        printf("
"); } }
1010 Just Skip The Problem(思考)
http://acm.hdu.edu.cn/showproblem.php?pid=6600
題意
数nをあげます.何回か聞いてみます.毎回数mを教えてください.n^mがmに等しいかどうか教えてくれます. 
最適解を聞いた場合に尋ねる方案数
考え方
最適解とは、順番に各案の数を聞くことです.膜数は1 e 6+3で、1 e 6+3より大きい木は0です.
コード
#include

#define ll long long
#define mod 1000003
#define inf 0x3f3f3f3f
using namespace std;

int main()
{
    ll n;
    while(~scanf("%lld",&n))
    {
        ll ans=1;
        if(n>=mod)
        {
            printf("0
"); continue; } for(ll i=1; i<=n; i++) ans=(ans*i)%mod; printf("%lld
",ans); } return 0; }
1011 Keen On Everything But Triangle(主席樹)
http://acm.hdu.edu.cn/showproblem.php?pid=6601
題意
n個の数mをあげます.毎回区間l-rの間で最大三角形の面積ができますか?
考え方
最も不成三角形の最適な状況はフィボナッチ数列で、区間の長さが40より大きいことが分かります.
問題は区間の第kの大きいsortがきっとタイムアウトします.主席の木を使うことができます.
コード
#include
#include 
#define ll long long
#include 
#include 
using namespace std;

const ll MAX = 112345;

struct Node
{
    ll sum, l, r;
} tree[MAX * 40];

ll root[MAX], cnt;
ll data[MAX];
vector sorted;

//     ID
ll get_id(ll x)
{
    return lower_bound(sorted.begin(), sorted.end(), x) - sorted.begin() + 1;
}

void init()
{
    cnt = 0;
    root[0] = 0;
}

//                  
ll create_node(ll po)
{
    ll idx = ++cnt;
    tree[idx].sum = tree[po].sum + 1;
    tree[idx].l = tree[po].l;
    tree[idx].r = tree[po].r;
    return idx;
}

void updata(ll &o, ll po, ll l, ll r, ll pos)
{
    o = create_node(po);
    if (l == r) return;
    ll m = (l + r) >> 1;
    if (pos <= m) updata(tree[o].l, tree[po].l, l, m, pos);
    else updata(tree[o].r, tree[po].r, m + 1, r, pos);
}

//    
ll query(ll s, ll e, ll l, ll r, ll k)
{
    if (l == r) return l;
    ll m = (l + r) >> 1;
    ll sum = tree[tree[e].l].sum - tree[tree[s].l].sum;
    if (k <= sum) return query(tree[s].l, tree[e].l, l, m, k);
    else return query(tree[s].r, tree[e].r, m + 1, r, k - sum);
}

int main()
{
    ll n, m;
    while (~scanf("%lld %lld", &n, &m))
    {
        init();
        for (ll i = 1; i <= n; i++)
        {
            scanf("%lld", &data[i]);
            sorted.push_back(data[i]);
        }
        sort(sorted.begin(), sorted.end());
        sorted.erase(unique(sorted.begin(), sorted.end()), sorted.end());
        ll num = sorted.size();
        for (ll i = 1; i <= n; i++)
            updata(root[i], root[i - 1], 1, num, get_id(data[i]));
        for(ll i=0; ic)
                {
                    ans=a+b+c;
                    break;
                }
            }
            printf("%lld
",ans); } } return 0; }
1012 Longest Subarray(線分樹)
http://acm.hdu.edu.cn/showproblem.php?pid=6602
題意
c kとnの数をあげます.条件を満たす一番長い区間の長さを求めます. 
条件 各数の個数>=kまたは0
考え方
列挙の各右端点を求めます.各左端点1-cの中で条件を満たす個数はどれぐらいですか?左端がcの点です.
コード 
#include 
using namespace std;
const int MAXN = 100005;
struct node
{
    int data,lazy;
}tree[MAXN*4];
int a[MAXN];
int n,c,k;
vector q[MAXN];
int maxn,qw;
void build(int x,int l,int r)
{
    tree[x].data = 0;
    tree[x].lazy = 0;
    if(l==r) return ;
    int mid = (l + r) / 2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
}
void pushdown(int rt)
{
    if(tree[rt].lazy)
    {
        tree[rt*2].lazy += tree[rt].lazy;
        tree[rt*2+1].lazy += tree[rt].lazy;
        tree[rt*2].data += tree[rt].lazy;
        tree[rt*2+1].data += tree[rt].lazy;
        tree[rt].lazy = 0;
    }
}
void updata(int l,int r,int rt,int L,int R,int x)
{
    if(l > r) return ;
    if(l == L&&r == R)
    {
        tree[rt].data += x;
        tree[rt].lazy += x;
        return ;
    }
    pushdown(rt);
    int mid = (L + R) / 2;
    if(mid >= r)
    {
        updata(l,r,rt*2,L,mid,x);
        tree[rt].data = max(tree[rt*2].data,tree[rt*2+1].data);
    }
    else if(mid < l)
    {
        updata(l,r,rt*2+1,mid+1,R,x);
        tree[rt].data = max(tree[rt*2].data,tree[rt*2+1].data);
    }
    else
    {
        updata(l,mid,rt*2,L,mid,x);
        updata(mid+1,r,rt*2+1,mid+1,R,x);
        tree[rt].data = max(tree[rt*2].data,tree[rt*2+1].data);
    }
}

void query(int l,int r,int rt,int L,int R)
{
    if(L==R)
    {
        if(tree[rt].data == c) qw = L;
        return ;
    }
    pushdown(rt);
    int mid = (L + R ) / 2;
    if(mid >= r)
    {
        if(tree[rt*2].data >= c) query(l,mid,rt*2,L,mid);
    }
    else if(mid < l)
    {
        if(tree[rt*2+1].data >= c) query(l,mid,rt*2+1,mid+1,R);
    }
    else
    {
        if(tree[rt*2].data >= c) query(l,mid,rt*2,L,mid);
        else if(tree[rt*2+1].data >= c) query(mid+1,r,rt*2+1,mid+1,R);
    }
}
int main()
{
//    freopen("in.txt","r",stdin);

    while(scanf("%d%d%d",&n,&c,&k) != EOF)
    {
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i = 1;i <= c;i++)
        {
            q[i].clear();
            q[i].push_back(0);
        }
        int ans=0;
        build(1,1,n);
        for(int i = 1;i <= n;i++)
        {
            int x = a[i];
            q[x].push_back(i);
            int siz = q[x].size();
            if(siz > k)
            {
                int l = q[x][siz-k-1]+1,r = q[x][siz-k];

                updata(l,r,1,1,n,1);
            }
            int l = q[x][siz-2]+1,r = i-1;

            updata(l,r,1,1,n,-1);

            updata(i,i,1,1,n,c-1);

            qw = 0;
            query(1,i,1,1,n);
            if(qw != 0)
            {
                ans = max(i-qw+1,ans);
            }

        }
        printf("%d
",ans); } return 0; }