【弱校胡策】2016.4.14(bzoj 2164)最短路+状圧DP+マトリックス乗算+Gauss消元+樹鎖断分+線分樹+リュックDP

29289 ワード

cyyz&qhyz&lwyz&gryz弱校胡策命題人:cyyz ws_fqk
T 3暴力写挫50+10+0滚太辣!
奇妙なデート(appointment.cpp/c/pas)
【問題の説明】
DQSとsxbはネット上で知り合ってからとても良い友达になって、しかも驚くべきOIレベルを持っています.NOI 2333では2人とも金メダルを獲得し、HU/PKUへの進出を保証した.そこで二人はこの喜大普奔の時に面基を行うことにした.NOI 2333は出場選手が多いので、n個の試験点を手配し、DQSは1番の試験点で、sxbはn番の試験点である.全国的な試合を開催する都市であるため、自然に多くの奇妙な性質がある:いくつかの試験点は1つの集合を構成し、集合中の試験点の2つの間の距離は固定値であり、1つの試験点は複数の集合に属することができる.形式的には、n個の考点、m個の集合があり、集合iにはs_が含まれている.i個の試験点、そしてこのs_i個の試験点の間の2つの距離はt_である.i. 金メダルのおじいさんの時間はすべてとても貴重で、今彼らは知りたいです.二人が同時に出発して、彼らが同じ試験点に着いて会うのに必要な最短時間はいくらですか.そして彼らは、満足時間が最も短い試験点がどれらなのか知りたいと思っています.
【入力】
最初の行には2つの整数n,mが含まれており、意味は題意の通りである.次にm行、まず行ごとに2つの整数があり、それぞれt_を表します.i,s_i,次にs_を含むi個の整数は、集合iに含まれるポイント番号を表す.
【出力】
二人が会えない場合は「impossible」と出力します.そうでなければ、最初の行は最短時間を表す整数を出力します.2行目は、最も短い時間を満たすポイント番号を出力し、隣接するものはスペースで区切られます.
【サンプル入力】
5 4
1 3 1 2 3
2 2 3 4
10 2 1 5
3 3 3 4 5

【サンプル出力】
3
3 4

【データ範囲】
50%のデータN<=1000に対してsigma(s_i)<=1000 100%のデータ2<=n<=10^5,1<=t_i<=10^9,s_i>0,sigma(s_i)<=10^6
n^2建辺50分
%学娣@Loi_aの玄学暴力はO 2 O 3を開く前提の下でAがこの問題を落とす...
各集合にこんなに多くのエッジを構築する必要はないことがわかります.新しい2つの点は、二次集合の入点、出点を表し、集合内の各点は入点建権値が0の辺に、出点は各点建権値が0の辺に、入点は出点建権値がtの辺に、最短2回走ればよい.
原題はdocが出した問題だそうです.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL; 
const int SZ = 3000010;
const int INF = 1000000010;

int head[SZ],nxt[SZ];

struct edge{
    int t;
    LL d;
}l[SZ];

void build(int f,int t,LL d)
{
    static int tot = 1;
    l[++ tot].t = t;
    l[tot].d = d;
    nxt[tot] = head[f];
    head[f] = tot;
}

LL dist1[SZ],dist2[SZ];

struct Heap{
    int u;
    LL d;
    Heap(int a = 0,LL b = 0) : u(a),d(b) {}
};

bool operator <(Heap a,Heap b)
{
    return a.d > b.d;
}

priority_queue<Heap> q;

bool vis[SZ];

void spfa(int s)
{
    memset(dist2,63,sizeof(dist2));
    memset(vis,0,sizeof(vis));
    dist2[s] = 0;
    q.push(Heap(s,0));
    while(q.size())
    {
        int u = q.top().u; q.pop();
        if(vis[u]) continue;
        vis[u] = 1; 
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(dist2[v] > dist2[u] + l[i].d)
            {
                dist2[v] = dist2[u] + l[i].d;
                q.push(Heap(v,dist2[v]));
            }
        }
    }
}

int main()
{
    freopen("appointment.in","r",stdin);
    freopen("appointment.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i ++)
    {
        int t,s;
        scanf("%d%d",&t,&s);
        int in = i + 100000,out = i + 100000 * 2;
        build(in,out,t);
        while(s --)
        {
            int x; scanf("%d",&x);
            build(x,in,0); build(out,x,0);
        }
    }
    spfa(1); 
    for(int i = 1;i <= n;i ++)
        dist1[i] = dist2[i];
    spfa(n);

    LL maxd = INF;
    for(int i = 1;i <= n;i ++)
        maxd = min(maxd,max(dist1[i],dist2[i]));
    if(maxd >= INF)
        { puts("impossible"); return 0; }
    printf("%lld
"
,maxd); for(int i = 1;i <= n;i ++) if(max(dist1[i],dist2[i]) == maxd) printf("%d ",i); fclose(stdin); fclose(stdout); return 0; }

不注意な特派員(careless.cpp/c/pas)
【問題の説明】
APIO&CTSC 2016のリストが発表されたとき、yzyとfqkは驚いたことにリストに彼らの名前がないことを発見し、急いで山東省特派員の豆包哥lpyに連絡した.仕方なく申込書が迷惑メールとしてゴミ箱に捨てられたと告げられた.不注意な特派員はいつも人を笑わせるようなことをします.例えば、選手の皆さんにメールを返信するとき、目がくらんで人を間違えることがよくあります.しかし、目がかすんでいるので、誤髪の範囲もあまり広くありません.具体的には、i番目の人に送るべきメールは、[i-2,i+2]という番号の人にうっかり送る可能性があります.例えば、NOIPリストの順番はlct 1999,Oxer,TA,fye,davidxuであるべきですが、TAに送るべきメールはこの5人の神のいずれかに送る可能性があります.しかし、彼は一人一人がメールを受け取ることを保証します.今、彼はn通のメールをn人の選手に送ると、どのような異なる結果が出るか知りたいと思っています.答えが大きいかもしれませんので、10^9+7を型抜きして出力してください.
【入力】
1行、整数n.
【出力】
1行、整数は答えを表します.
【サンプル入力】
4

【サンプル出力】
14

【データ範囲】
10%のデータに対して、n<=10は40%のデータに対して、n<=500000は100%のデータに対して、n<=10^16
i番目の数は[i-2,i+2]の区間内に置くことができ,長さnの数列の合法的な配列スキーム数を求める.
出題者が与えるやり方は,状圧DPであり,dp[i][S]は現在i番目の数に記入されていることを示し,[i-2,i+2]記入/記入されていない状態がSのシナリオ数である.[1,i−3]を常に1に保つことで,移行できる.
各遷移は同じであり,行列最適化が可能であることが分かった.32*32の行列.
玄学のやり方:暴力は上位10項目を飛び出して、それから強引に係数を設定してガウスは元を消します...最後にf[n]=2 f[n-1]+2[n-3]-f[n-5]...事実はこれが正しいことを証明して、それから行列はいいです.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL; 
const int SZ = 500010;
const int INF = 1000000010;
const int mod = 1000000007;

struct matrix{
    int n,m;
    int num[20][20];
    matrix(int a = 0,int b = 0) :n(a),m(b) { memset(num,0,sizeof(num)); }
};

matrix operator *(const matrix &a,const matrix &b)
{
    matrix ans(a.n,b.m);
    for(int i = 1;i <= ans.n;i ++)
        for(int j = 1;j <= ans.m;j ++)
            for(int k = 1;k <= a.m;k ++)
                ans.num[i][j] = (ans.num[i][j] + (LL)a.num[i][k] * b.num[k][j] % mod) % mod;
    return ans;
}

matrix ans,f;

matrix ksm(matrix a,LL b)
{
    while(b)
    {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

void init()
{
    ans.n = 1; ans.m = 5;
    f.n = 5; f.m = 5;
    ans.num[1][1] = 31; ans.num[1][2] = 14; ans.num[1][3] = 6;  ans.num[1][4] = 2;  ans.num[1][5] = 1;

    f.num[1][1] = 2;    f.num[3][1] = 2;    f.num[5][1] = -1;
    f.num[1][2] = 1;    f.num[2][3] = 1;    f.num[3][4] = 1;    f.num[4][5] = 1;
}

int main()
{
    freopen("careless.in","r",stdin);
    freopen("careless.out","w",stdout);
    LL n;
    scanf("%lld",&n);
    init();
    if(n <= 5) printf("%d
"
,ans.num[1][5 - n + 1]); else { ksm(f,n - 5); printf("%d
"
,(ans.num[1][1] + mod) % mod); } return 0; } /* 1 1 2 2 3 6 4 14 5 31 f[n] = 2f[i - 1] + 2 * f[i - 3] - f[i - 5] */

Description
がらんとしたcg大軍は鉱物資源が極めて豊富な都市を発見し、彼らはこの都市で新しい採鉱戦略を実施しようとした.この都市はn個のノードがある根木と見なすことができ,各ノードを1〜nの整数で番号付けする.便宜上、いずれの非ルートノードvについても、いずれの祖先の番号もvよりも厳格に小さい.ツリーの各ノードは鉱山を表し、各エッジは街を表します.cg大軍の小さな隊長として、あなたはm人の部下を持っています.i行j列目のデータをTi,jで表す2次元の動的情報テーブルがあります.ある地域の採掘を許可されると、部下を各鉱山に割り当てることができます.i番目の鉱点にj人を配置するとTi,j単位の鉱物を得ることができる.採掘が許可されている地域は、一対の鉱点(u,v)をあげます.vはuの祖先であることを保証する(ここでは祖先がu自身を含むことを定義する);uはあなたのために制御する領域で、uを根とするサブツリーの上で任意に部下を割り当てることができる;uからvの簡単な経路(uは含まれないがvは含まれ、u=vはuを含む)は探検経路であり、この経路では複数の鉱点まで部下を手配することができる.今回の採掘の収益は部下の鉱点を手配する収益の和である.
Input
入力された最初の行は、5つの正の整数n、m、A、B、Qを含む.nは鉱点の個数,mは部下の数である.A,B,Qは動的情報テーブルに関するデータである.2行目はn−1個の正の整数を含み,i番目の数はFi+1であり,ノードi+1の父親を表す.次に、以下の方法でn組のデータを順次生成する必要があります.各組のデータはm個です.ここで、i番目のグループのm個のデータは、情報テーブルのi番目の行のm個のデータである.次の行には、イベントの数を表す正の整数Cが含まれます.最後にC行が与えられ、各行はイベントを記述する.各イベントには、0または1の整数が最初に与えられます.この数が0の場合、後に正の整数pがあり、動的情報テーブルが更新されたことを示します.情報テーブルのp行目のm個のデータを置き換えるために、m個のデータのセットを生成する必要があります.この数が1の場合、後ろに2つの正の整数u、vがあり、採掘可能な領域が現れたことを示し、今回の採掘の収益に答える必要があります.同じ行の各数を1つのスペースで区切り、余分なスペースや改行はありません.データの生成方法は以下の通りである:m個の小さいから大きいまで配列されたデータのセットを生成するたびに、動的情報テーブルの行を置き換える.ここで、情報テーブルのj番目の列の数は、小さいから大きいまで置換される.次のコードm回を呼び出し、並べ替えてデータのセットを得る.(注意重複数が発生する可能性がある)関数GetInt A←((A xor B)+(B div X)+(B*X))and Y B←((A xor B)+(A div X)+(A*X))and Y戻り(A xor B)mod Q A,B,Qはいずれも32ビットの符号付き整数で保存(C/C++のsigned long intタイプ,pascalのlongintタイプ),X=216(2の16次方),Y=231-1(2の31次方-1),xorはビット異または演算であるdivは整除演算,andはビットかつ演算,modは取余演算である.低31ビットしか残っていないため、データのオーバーフローの問題を考慮する必要はありません.(AとBが変わるたびに注意)
Output
各採掘イベント(先頭が1のイベント)に対して、収益のたびに1行1つの整数を出力します.
Sample Input
10 5 1 2 10

1 1 3 3 4 4 6 6 9

4

1 6 3

1 9 1

0 1

1 1 1

Sample Output
11

9

12

【サンプル説明】
最初の情報表は以下の通りです.
1   2   3   4   5

1   0   1   1   2   2

2   0   5   7   7   9

3   1   2   3   4   5

4   0   1   2   4   5

5   2   4   7   8   8

6   0   2   3   8   9

7   1   3   5   6   8

8   3   3   3   7   8

9   0   1   2   3   9

10  0   0   1   4   4

変化後の第1の行為
1   1   1   1   4   7

1回目の採掘は、鉱点6、8、9、10の任意の配置で行うことができ、鉱点3または4のいずれかの配置採掘を選択することができる.1つの最適な手配は、鉱山6で4人、鉱山8で1人を手配することです.2回目の採掘は、鉱点9に配置することができ、鉱点6、4、3、1の中から1つの配置を選択することができる.1つの最適な手配は、鉱山9で1人、鉱山6で4人を手配することです.
HINT
50%のデータがあり、2≦i≦nを満たす整数iに対して、Fi=i−1である.これらのデータのうち40%のデータ(すなわち全データの20%)がn≦500,m≦20,C≦500を満たしている.上記のデータを除き、40%のデータがn≦500,m≦20,C≦500を満たしている.100%のデータに対して1≦n≦20000,1≦m≦50,1≦C≦2000である.2≦i≦nを満たす整数iに対して、1≦FiSource
問題面が長すぎて、コピーしたbzojの.
オレンジ:http://www.tsinsen.com/A1219.
線分木は各ポイントメンテナンス現在の区間に1つのポイントを選んでi個人の最大価値を置いて、もう1つは勝手にiポイントの最大価値を置いて、それから合併する時リュックサックのように合併すればいいです
毒腫問題.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL; 
const int SZ = 100010;
const int INF = 1000000010;

int n,m,A,B,Q;

int get_int()
{
    const int X = 1 << 16;
    const int Y = (1ll << 31ll) - 1ll;
    A=((A^B)+(B/X)+(B*X))&Y;
    B=((A^B)+(A/X)+(A*X))&Y;
    return (A^B)%Q;
}

void scan(int &n)
{
    n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0'; a = getchar(); }
    if(flag) n = -n;
}

int head[SZ],nxt[SZ],to[SZ];

void build(int f,int t)
{
    static int tot = 1;
    to[++ tot] = t;
    nxt[tot] = head[f];
    head[f] = tot;
}

int top[SZ],sz[SZ],son[SZ],fa[SZ],deep[SZ];

void dfs_1(int u,int f)
{
    fa[u] = f;
    deep[u] = deep[f] + 1;
    sz[u] = 1;
    for(int i = head[u];i;i = nxt[i])
    {
        int v = to[i];
        dfs_1(v,u);
        sz[u] += sz[v];
        if(!son[u] || sz[son[u]] < sz[v]) son[u] = v;
    }
}

int pre[SZ],suf[SZ],dfs_clock = 0,intre[SZ];

void dfs_2(int u,int topu)
{
    top[u] = topu;
    pre[u] = ++ dfs_clock;
    intre[dfs_clock] = u;
    if(son[u]) dfs_2(son[u],topu);
    for(int i = head[u];i;i = nxt[i])
    {
        int v = to[i];
        if(v == son[u]) continue;
        dfs_2(v,v);
    }
    suf[u] = ++ dfs_clock;
}

int val[SZ][60];

struct segment{
    int l,r;
    int mx1[60],mx2[60]; //     
}tree[SZ << 2];

void update(int p)
{
    memset(tree[p].mx1,0,sizeof(tree[p].mx1));
    int lch = p << 1,rch = p << 1 | 1;
// for(int i = 0;i <= m;i ++)
// for(int j = 0;j <= m - i;j ++)
// tree[p].mx1[i + j] = max(tree[p].mx1[i + j],tree[lch].mx1[i] + tree[rch].mx1[j]);
    for(int i = m;i >= 0;i --)
        for(int j = i;j >= 0;j --)
            tree[p].mx1[i] = max(tree[p].mx1[i],tree[lch].mx1[j] + tree[rch].mx1[i - j]);

    for(int i = 1;i <= m;i ++) tree[p].mx2[i] = max(tree[lch].mx2[i],tree[rch].mx2[i]);
}

void build(int p,int l,int r)
{
    tree[p].l = l; tree[p].r = r;
    if(l == r)
    {
        for(int i = 1;i <= m;i ++)
            tree[p].mx1[i] = tree[p].mx2[i] = val[intre[l]][i];
        return ;
    }
    int mid = (l + r) >> 1;
    build(p << 1,l,mid); build(p << 1 | 1,mid + 1,r);
    update(p);
}

void change(int p,int pos)
{
    if(tree[p].l == tree[p].r)
    {
        for(int i = 1;i <= m;i ++)
            tree[p].mx1[i] = tree[p].mx2[i] = val[intre[pos]][i];
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(pos <= mid) change(p << 1,pos);
    else change(p << 1 | 1,pos);
    update(p);
}

int ans1[60],ans2[60];

void ask_ans1(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)
    {
        for(int i = m;i >= 0;i --)
            for(int j = i;j >= 0;j --)
                ans1[i] = max(ans1[i],ans1[i - j] + tree[p].mx1[j]);
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid) ask_ans1(p << 1,l,r);
    if(mid < r) ask_ans1(p << 1 | 1,l,r);
}

void ask_ans2(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)
    {
        for(int i = 1;i <= m;i ++)
            ans2[i] = max(ans2[i],tree[p].mx2[i]);
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid) ask_ans2(p << 1,l,r);
    if(mid < r) ask_ans2(p << 1 | 1,l,r);
}

void find_ans2(int x,int y)
{
    int fx = top[x],fy = top[y];
    while(fx != fy)
    {
        if(deep[fx] < deep[fy]) swap(fx,fy),swap(x,y);
        ask_ans2(1,pre[fx],pre[x]);
        x = fa[fx]; fx = top[x];
    }
    if(deep[x] > deep[y]) swap(x,y);
    ask_ans2(1,pre[x],pre[y]);
}

int main()
{
    freopen("energy.in","r",stdin);
    freopen("energy.out","w",stdout);       
    scan(n); scan(m); scan(A); scan(B); scan(Q);
    for(int i = 2;i <= n;i ++)
    {
        int x;
        scan(x);
        build(x,i);
    }
    for(int i = 1;i <= n;i ++)
    {
        for(int j = 1;j <= m;j ++)
            val[i][j] = get_int();
        sort(val[i] + 1,val[i] + 1 + m);
    }
    dfs_1(1,0); dfs_2(1,1);
    build(1,1,dfs_clock);
    int C;
    scan(C);
    while(C --)
    {
        int opt;
        scan(opt);
        if(opt == 0)
        {
            int p;
            scan(p);
            for(int j = 1;j <= m;j ++)
                val[p][j] = get_int();
            sort(val[p] + 1,val[p] + 1 + m);
            change(1,pre[p]);
        }
        else
        {
            memset(ans1,0,sizeof(ans1));
            memset(ans2,0,sizeof(ans2));
            int u,v;
            scan(u); scan(v);
            ask_ans1(1,pre[u],suf[u]);
            if(u != v) find_ans2(v,fa[u]);
            int ans = 0;
            for(int i = 0;i <= m;i ++)
                ans = max(ans,ans1[i] + ans2[m - i]);
            printf("%d
"
,ans); } } fclose(stdin); fclose(stdout); return 0; }