2018CCPC吉林赛区 hdu6555~hdu6566

2018CCPC吉林赛区(重现赛)- 感谢北华大学

A

基础数论。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

int main() {
    int T,cc = 0;
    scanf("%d",&T);
    while(T --) {
        int n;
        scanf("%d",&n);
        int ans = 0;
        for(int i = 1,r;i <= n;i = r + 1) {
            r = n / (n / i);
            ans += (r-i+1ll) * (n / i) & 1;
        }
        if(ans&1) printf("Case %d: odd\n",++ cc);
        else printf("Case %d: even\n",++ cc);
    }
}

B

基础模拟。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

int main() {
    int T,cc = 0;
    scanf("%d",&T);
    map<string,int> mp;
    mp["Beijing"] = 8;
    mp["Washington"] = -5;
    mp["London"] = 0;
    mp["Moscow"] = 3;
    while(T --) {
        int h,m;
        string s,name1,name2;
        scanf("%d:%d",&h,&m);
        cin >> s >> name1 >> name2;
        h %= 12;
        if(s == "PM") h += 12;
        int delta = mp[name2] - mp[name1];
        h += delta;
        //cout << h << endl;
        string rq;
        if(h >= 24) rq = "Tomorrow";
        else if(h < 0) rq = "Yesterday";
        else rq = "Today";
        h += 24; h %= 24;
        string ans;
        if(h <= 11) ans = "AM";
        else ans = "PM",h -= 12;
        if(h == 0) h = 12;
        printf("Case %d: %s %d:%02d %s\n",++ cc,rq.c_str(),h,m,ans.c_str());
    }
}

C

基础贪心。也可以写并查集。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
const int SZ = 2e5 + 10;

int bel[SZ],tot,sum,mark[SZ];
vector<int> g[SZ];

void dfs(int u,int c) {
    bel[u] = c;
    for(int v : g[u]) {
        dfs(v,c);
    }
}

int main() {
    int T,cc = 0;
    scanf("%d",&T);
    while(T --) {
        int n;
        scanf("%d",&n);
        for(int i = 1;i <= tot;i ++) {
            bel[i] = 0;
            mark[i] = 0;
            g[i].clear();
        }
        tot = n,sum = 0;

        priority_queue<pii> q;
        for(int i = 1;i <= n;i ++) {
            int x;
            scanf("%d",&x);
            q.push(make_pair(x,i));
        }
        while(q.size()) {
            pii p = q.top(); q.pop();
            if(p.first <= 1) { mark[p.second] = 1; sum ++; continue; }
            if(q.size()) {
                pii p2 = q.top(); q.pop();
                if(p2.first == p.first) {
                    pii ans = make_pair(p.first-1,++ tot);
                   //printf("%d -> %d\n",tot,p.second);
                   // printf("%d -> %d\n",tot,p2.second);
                    g[tot].push_back(p.second);
                    g[tot].push_back(p2.second);
                    q.push(ans);
                }
                else q.push(p2);
            }
        }
        if(sum < 2) {
            printf("Case %d: NO\n",++ cc);
        }
        else {
            printf("Case %d: YES\n",++ cc);
            int t = 0;
            for(int i = 1;i <= tot;i ++) {
                if(mark[i]) {
                    dfs(i,t);
                    if(t==0) t++;
                }
            }
            for(int i = 1;i <= n;i ++) printf("%d",bel[i]); puts("");
        }
    }
}

D

基础概率DP。由于会加1.5%所以状态要乘2。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
const int SZ = 2e5 + 10;

double f[333];


int main() {
    int T,cc = 0;
    scanf("%d",&T);
    while(T --) {
        int pp;
        scanf("%d",&pp);
        double p = 1.0*pp/100;
        f[200] = 1/p;
        for(int i = 199;i >= 4;i --) {
            int nx1 = min(200,i+4);
            int nx2 = min(200,i+3);
            f[i] = p * (i/200.0+(1-i/200.0)*(f[nx1]+1))
                  + (1-p) * (f[nx2] + 1);
        }
        printf("Case %d: %.10f\n",++ cc,f[4]);
    }
}


E

推公式数学题。解方程。直接套求根公式比魔改求根公式的精度要高(?)

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN=1e3+10;
const int mod=1e9+7;
const double eps=1e-7;

int sgn(double a){
    if (fabs(a)<eps) return 0;
    if (a>0) return 1;
    return -1;
}

int main(){
    int T,cas=0; scanf("%d",&T);
    while(T--){
        double r,h;
        scanf("%lf%lf",&r,&h);
        double ppx,ppy,ppz,ddx,ddy,ddz;
        scanf("%lf%lf%lf",&ppx,&ppy,&ppz);
        scanf("%lf%lf%lf",&ddx,&ddy,&ddz);
        long double px=ppx,py=ppy,pz=ppz,dx=ddx,dy=ddy,dz=ddz;
        long double ox=px,oy=py,oz=pz;
        long double k = r*r/h/h;
        long double a = dx * dx + dy * dy - k * dz * dz;
        long double b = 2 * (dx * ox + dy * oy - k * dz * (oz - h));
        long double c = ox * ox + oy * oy - k * (oz - h) * (oz - h);
        long double discrim = b * b - 4 * a * c;
        long double rootDiscrim = sqrt(discrim);
        /*long double q;
        if (b < 0) q = (-b + rootDiscrim)/2;
        else q = (-b - rootDiscrim)/2;
        long double t0 = q / a;
        long double t1 = c / q;
        long double ans=min(t0,t1);
        if(ans < 1e-8) ans = 0;*/
        long double ans = (-b-rootDiscrim)/2/a;
        printf("Case %d: %.10f\n",++cas,(double)ans);
    }
    return 0;
}

F

因为左端点递增,所以 i 包含 k 那么 j 一定包含 k,所以 j 取 i-1 最优,每个点的答案就是 x-2。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
const int SZ = 2e5 + 10;
const int mod = 1e9+7;

LL read() {
    LL 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;
    return n;
}

int main() {
    int T = read(),cc = 0;
    while(T --) {
        int n = read(),ans = 0;
        for(int i = 1;i <= n;i ++) {
            int x = read();
            x = max(x-2,0);
            ans ^= x;
        }
        printf("Case %d: %d\n",++ cc,ans);
    }
}

G

题意:给 \(10^4\) 个一欧姆的电阻。定义一个元件:

  1. 一个一欧姆电阻是一个元件
  2. 两个元件并联是一个元件
  3. 两个元件串联是一个元件

构造一个元件,使得其电阻阻值与 \(r\) 的绝对值之差小于 \(10^{-7}\)

key:随机,乱搞

有连分数做法,不过不太会……

众所周知 n 个 a 欧姆的电阻并联的电阻是 a/n。想要得到一个尽量小的电阻那么一定是若干个一欧姆电阻并联。

考虑把 r 拆分成若干个 1/ki,这其实是埃及分数。我们的目标是使得 \(\sum k_i \le 10^4\)

考虑加入一个 1/ki,那么能算出 ki 的下界。每次选下界的贪心是不优的,而我们只需要找到一个较优解。

实际上只需要给下界加一个随机数调整,跑若干遍直到满足条件即可。实测大概跑个几遍就有了,随机了 \(10^5\) 组都能出解。

输出比较坑

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
const int SZ = 2e5 + 10;
const int mod = 1e9+7;

LL read() {
    LL 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;
    return n;
}

mt19937 rd(2333);

LL randlr(LL l,LL r)  {
    return rd()%(r-l+1)+l;
}

vector<int> a;

int work(double r) {
    a.clear();
    double now = 0;
    int sum = 0;
    while(fabs(now-r) > 1e-9) {
        int k = 1/(r-now); if((r-now)*k<1) k++;
        k += randlr(0,5);
        now += 1.0/k;
        sum += k;
        a.push_back(k);
      //  printf("%.10f %d\n",now,k);
    }
   // puts("finish");
    return sum;
}

int main() {
    int T = read(),cc = 0;
    while(T --) {
//        double r = randlr(1e7,9e7)/1e7;
        double r;
        scanf("%lf",&r);
        printf("Case %d:\n",++ cc);
        for(int i = 1;;i ++){
            int sum = work(r);
            if(sum <= 1e4) {
                int m = sum - 1;
                printf("%d %d\n",sum,m);
                int t = sum-1,lst = 0;
             //   for(int x : a) cout << x << " "; puts("");
                vector<int> b;
                for(int x : a) {
                    printf("1 %d %d\n",lst,lst+1);
                    lst+=2; t ++; x --;
                    while(x -- > 1) printf("1 %d %d\n",lst,t),lst++,t++;
                    b.push_back(t);
                }
                int x = a.size();
                if(x>1) {
                    printf("0 %d %d\n",b[0],b[1]); t++;
                    for(int j = 2;j < b.size();j ++)
                        printf("0 %d %d\n",b[j],t),t++;
                }
                break;
            }
        }
    }
}

H

基础线段树。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
const int SZ = 2e5 + 10;
const int mod = 1e9+7;

LL read() {
    LL 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;
    return n;
}

int mi[SZ];

struct seg {
    int l,r;
    int sum,misum;
    int x,rx,len;
}tree[SZ*4];

void update(int p) {
    tree[p].sum = (tree[p<<1].sum+tree[p<<1|1].sum) % mod;
    tree[p].misum = (tree[p<<1].misum+tree[p<<1|1].misum) % mod;
}

void build(int p,int l,int r) {
    tree[p].l = l;
    tree[p].r = r;
    tree[p].sum = 0;
    tree[p].misum = 0;
    tree[p].x = 0;
    tree[p].rx = 0;
    tree[p].len = 0;
    if(l == r) {
        tree[p].misum = 1;
        return ;
    }
    int mid = l + r >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    update(p);
}

void pushc(int p,int x,int rx,int len) {
    tree[p].sum = (1ll*mi[len]*tree[p].sum%mod+1ll*x*(tree[p].r-tree[p].l+1)%mod
        + 1ll*rx*tree[p].misum%mod*mi[len]%mod) % mod;
    tree[p].misum = 1ll*mi[len*2]*tree[p].misum % mod;
    tree[p].x = (1ll*tree[p].x*mi[len]%mod + x) % mod;
    tree[p].rx = (tree[p].rx + 1ll*rx*mi[tree[p].len] % mod) % mod;
    tree[p].len += len;
}

void pushdown(int p) {
    if(tree[p].len) {
        pushc(p<<1,tree[p].x,tree[p].rx,tree[p].len);
        pushc(p<<1|1,tree[p].x,tree[p].rx,tree[p].len);
        tree[p].len = 0;
        tree[p].x = 0;
        tree[p].rx = 0;
    }
}

void change(int p,int l,int r,int d) {
    if(l<=tree[p].l&&tree[p].r<=r) {
        pushc(p,d,d,1);
        return ;
    }
    pushdown(p);
    int mid = tree[p].l + tree[p].r >> 1;
    if(l <= mid) change(p<<1,l,r,d);
    if(mid < r) change(p<<1|1,l,r,d);
    update(p);
}

int ask(int p,int l,int r) {
    if(l<=tree[p].l&&tree[p].r<=r) {
        return tree[p].sum;
    }
    pushdown(p);
    int mid = tree[p].l + tree[p].r >> 1,ans = 0;
    if(l <= mid) (ans += ask(p<<1,l,r)) %= mod;
    if(mid < r) (ans += ask(p<<1|1,l,r)) %= mod;
    return ans;
}

int main() {
    mi[0] = 1;
    for(int i = 1;i <= 2e5;i ++) mi[i]=mi[i-1]*10ll%mod;
    int T = read(),cc = 0;
    while(T--) {
        int n = read(),m = read();
        build(1,1,n);
        printf("Case %d:\n",++ cc);
        while(m --) {
            char s[6];
            scanf("%s",s);
            int l = read(),r = read();
            if(s[0] == 'w') {
                int d = read();
                change(1,l,r,d);
            }
            else {
                printf("%d\n",ask(1,l,r));
            }
        }
    }
}

I

要么全打完,那么肯定用刚好打过这个怪的打他。要么不打完,那么防御怪都不打,剩下的田忌赛马。

J

题意:给 n 个球, m 个颜色,给出每个球染每个颜色的权值。染完色之后要重排成一个环,使得存在一组重排方案使得任意两个相邻球颜色不同。求权值极差最小值。 \(n,m \le 2000,a_{i,j}\le 10^6\)

key:尺取

题意即为任意颜色不能用超过 n/2 次。

考虑枚举最小值,找到对应的最小的最大值。现在即如何判定是否合法。

如果已知了最小值和最大值,现在要判定。

显然每个球要至少有一个可选颜色。

如果一个球只有一个颜色可选,那么这个球就只能是这个颜色。也就是说对于每个颜色,必选它的球的数目必须小于等于 n/2。现在所有的颜色必选它的球的数目都满足条件,如果对于所有颜色,可选它的球的数目小于等于 n/2,那么一定存在一组方案(显然);否则,即存在一个颜色,可选它的球的数目大于 n/2,以下分类讨论:

  1. 当 n 是偶数时,因为必选它的球的数目小于等于 n/2,那么我们可以恰好选它 n/2个,用其它颜色补剩下的(因为其他颜色不可能大于n/2)。
  2. 当 n 是奇数时,此时补全 n/2 后,剩下的球全选另一个颜色会出问题。而这种情况只会出现在仅用两个颜色时(如果用大于等于三种颜色,那么一定有一个可选颜色选第三种,不会出现这种情况)。

仔细观察上述的充要条件,我们我们只需要维护这么几个东西:

  1. 没有可选颜色的球的个数
  2. 对于每个颜色,必选它的球的个数
  3. 有多少个颜色,必选它的个数大于 n/2
  4. 有多少个颜色可选

对权值排序做双指针,每次是单点插入和删除,上述几个都可以 \(O(1)\) 实现。(对于每个球,加入颜色和删除颜色是按权值递增的;对于每个颜色,每次是必选它的球的个数加一或减一),所以瓶颈就是排个序。你可以用桶排做到 \(O(10^6+nm)\),不过没必要。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
typedef pair<int,LL> pil;
const int SZ = 2e5 + 10;
const int mod = 1e9+7;

LL read() {
    LL 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;
    return n;
}

int a[SZ],num,lim,colNum,col2[SZ];

void addCol(int x) {
   // printf("add color: %d\n",x);
    a[x] ++;
    if(a[x] == lim) num ++;
}

void delCol(int x) {
   // printf("del color: %d\n",x);
    if(a[x] == lim) num --;
    a[x] --;
}

int noMatchNumber;
queue<int> cols[SZ];

void init(int n,int m) {
    for(int i = 1;i <= n;i ++) {
        while(cols[i].size()) cols[i].pop();
    }
    noMatchNumber = n;
    for(int i = 1;i <= m;i ++) {
        a[i] = 0;
        col2[i] = 0;
    }
    colNum = 0;
    num = 0;
    lim = n / 2 + 1;
}

void add(pii p) {
    int id = p.first,col = p.second;
    if(col2[col] == 0) colNum ++;
    col2[col] ++;
    if(cols[id].size() == 0) {
        noMatchNumber --;
        addCol(col);
    }
    else if(cols[id].size() == 1) {
        delCol(cols[id].front());
    }
    cols[id].push(col);
}

void del(pii p) {
    int id = p.first,col = p.second;
    if(col2[col] == 1) colNum --;
    col2[col] --;
    assert(!cols[id].empty());
    assert(cols[id].front() == col);
    if(cols[id].size() == 1) {
        noMatchNumber ++;
        delCol(cols[id].front());
    }
    else if(cols[id].size() == 2) {
        addCol(cols[id].back());
    }
    cols[id].pop();
}

int n,m;

bool check() {
    if(noMatchNumber) return false;
    if(num) return false;
    if(n%2==1&&colNum <= 2) return false;
    return true;
}

int main() {
    int T = read(),cc = 0;
    while(T --) {
        n = read(),m = read();
        init(n,m);
        vector<pair<int,pii> > a;
        for(int i = 1;i <= n;i ++) {
            for(int j = 1;j <= m;j ++) {
                a.push_back(make_pair(read(),make_pair(i,j)));
            }
        }
        sort(a.begin(),a.end());
        int ans = 1e9;
        //for(int i = 0;i < a.size();i ++) printf("%d: %d %d %d\n",i,a[i].first,a[i].second.first,a[i].second.second);
        for(int i = 0,j = -1;i < a.size();i ++) {
            while(!check() && j+1 < (int)a.size()) add(a[j+1].second),j ++;
            if(!check()) break;
          //  printf("[%d,%d] %d\n",i,j,num);
            ans = min(ans,a[j].first - a[i].first);
            del(a[i].second);
        }
        if(ans == 1e9) ans = -1;
        printf("Case %d: %d\n",++ cc,ans);
    }
}

/**
2
3 3
1 2 100
2 1 200
1 1 300

2 2

1
5 3
2 3 7
2 5 7
2 7 5
2 6 5
7 6 8


4 1
1
1
1
1


*/

K

辣鸡模拟,咕

L

题意:给一个 n 个点的树,每个点有一个重量和价值。可以从树中选出一个独立集,定义 \(f(x)\) 为总重量为 x 的最大价值的方案数,求所有的 \(f(x) (1 \le x \le m)\)\(n \le 50,m \le 5000\)

key:搜索,背包

先放个题解的 \(O(n^2m)\) 的做法:

普通树形背包复杂度 O(nm2),不可取。
考虑在 dfs 序上做 01 背包。

对整棵树做轻链剖分, dfs 先走轻儿子,最后再走重儿
子。这样从重儿子子树回溯的时候会直接回到重链顶端
我们所选独立集的后续发展与这条重链的中间节点是否
在独立集内没有关系。

从而背包是我们只要记下该节点的 log(n) 个重链顶端是
否在独立集内,该状态的大小为 2^log(n) = n。 dpi,mask,m 表
示走到 i 这个节点, log(n) 个重链顶端的状态为 mask,
所选重量为 m 的最有方案,直接背包。

没太懂那个 log 个重链……所以放个乱搞做法。

考虑折半,瓶颈在于对于两个点集的合并如何 check 是否是独立集。如果能模拟暴力背包的过程,那么只需要记录子树根节点选不选即可,这样合并两个背包是 max 卷积, \(O(m^2)\) 的复杂度。

考虑选重心为根节点,对每个子树暴力搜索独立集得到一个背包,之后按子树根节点选不选来讨论合并。这样能保证搜索最坏情况下是 \(2^{25}\) ,现在来看合并次数。

假设重心的每个子树大小都是 k ,那么搜索独立集的复杂度是 \(O(2^kn/k)\) ,迭代合并的复杂度是 \(O(nm^2/k)\) ,取等号时 k 在 25 左右,如果乘个 T 可能就不太行了。这个最坏情况是链。

子树比较小时可以用一些常数优化,比如只合并背包中合法的元素,合并子树时从小到大之类。

然而数据并没有卡这个乱搞所以就200+ms过了,我估计出题人也没想到

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
typedef pair<int,LL> pil;
const int SZ = 2e5 + 10;
const int mod = 1e9+7;

LL read() {
    LL 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;
    return n;
}

int n,m;
int A[55],B[55];
int mark[55],xl[55];
vector<int> nodelist,g[55];

void remark(int u,int f) {
    nodelist.push_back(u);
    for(int v : g[u]) {
        if(v == f) continue;
        remark(v,u);
    }
}

int sz[55];

void predfs(int u,int f) {
    sz[u] = 1;
    for(int v : g[u]) {
        if(v == f) continue;
        predfs(v,u);
        sz[u] += sz[v];
    }
}

int get_centroid(int u) {
    predfs(u,0);
    int s = sz[u];
    while(1) {
        int found = 1;
        for(int v : g[u]) {
            if(sz[v] > sz[u]) continue;
            if(sz[v] * 2 >= s) {
                u = v; found = 0; break;
            }
        }
        if(found) return u;
    }
}

pil up(pil a,pil b) {
    pil ans;
    ans.first = max(a.first,b.first);
    if(a.first == ans.first) ans.second += a.second;
    if(b.first == ans.first) ans.second += b.second;
    if(!ans.second) ans.first = -1;
    return ans;
}

pil a[2][5010];
pil ans[2][5010];
pil tmp[2][5010];

void dfs(int d,int dn,int S,int sa,int sb) {
    if(sa>m) return ;
    if(d == dn) {
        a[S&1][sa] = up(a[S&1][sa],make_pair(sb,1));
        return ;
    }
    int u = nodelist[d];
    if(!(xl[u] & S)) dfs(d+1,dn,S^(1<<mark[u]),sa+A[u],sb+B[u]);
    dfs(d+1,dn,S,sa,sb);
}

void work(int u,int r) {
    nodelist.clear();
    remark(u,r);
    for(int i = 0;i < nodelist.size();i ++) mark[nodelist[i]] = i;
    for(int u : nodelist) {
        xl[u] = 0;
        for(int v : g[u]) {
            if(v == r) continue;
            xl[u] ^= 1<<mark[v];
        }
    }
    for(int i = 0;i < 2;i ++)
        for(int j = 0;j <= m;j ++) {
            tmp[i][j] = make_pair(-1,0);
            a[i][j] = make_pair(-1,0);
        }
    dfs(0,nodelist.size(),0,0,0);
    for(int x = 0;x < 2;x ++) {
        for(int i = 0;i <= m;i ++) {
            if(ans[x][i].second == 0) continue;
            for(int y = 0;y < 2;y ++) {
                if(x==1&&y==1) continue;
                for(int j = 0;i+j <= m;j ++) {
                    if(a[y][j].second == 0) continue;
                    int sb = ans[x][i].first+a[y][j].first;
                    LL t = ans[x][i].second*a[y][j].second;
                    tmp[x][i+j] = up(tmp[x][i+j],make_pair(sb,t));
                }
            }
        }
    }
    /*cout << u << endl;
    for(int i = 0;i < 2;i ++) {
        cout << i << endl;
        for(int j = 0;j <= m;j ++) printf("%3d",a[i][j].first); puts("");
        for(int j = 0;j <= m;j ++) printf("%3d",a[i][j].second); puts("");
    }
    for(int i = 0;i < 2;i ++) {
        cout << i << endl;
        for(int j = 0;j <= m;j ++) printf("%3d",ans[i][j].first); puts("");
        for(int j = 0;j <= m;j ++) printf("%3d",ans[i][j].second); puts("");
    }
    for(int i = 0;i < 2;i ++) {
        cout << i << endl;
        for(int j = 0;j <= m;j ++) printf("%3d",tmp[i][j].first); puts("");
        for(int j = 0;j <= m;j ++) printf("%3d",tmp[i][j].second); puts("");
    }
    puts("--------");*/
    for(int i = 0;i < 2;i ++)
        for(int j = 0;j <= m;j ++)
            ans[i][j] = tmp[i][j];
}

void bl() {
    vector<pil> ans; ans.resize(m+1);
    for(int S = 0;S < (1<<n);S ++) {
        int sb = 0,sa = 0,b = 0;
        for(int i = 0;i < n;i ++) {
            if(S>>i&1) {
                bool flag = 0;
                for(int j : g[i+1]) if(S>>(j-1)&1) flag = 1;
                if(flag) { b=1; break;}
                sa += A[i+1];
                sb += B[i+1];
            }
        }
        if(b) continue;
        if(sa>m) continue;
        if(ans[sa].first < sb) ans[sa] = make_pair(sb,1);
        else if(ans[sa].first == sb) ans[sa].second ++;
    }
    for(int i = 1;i <= m;i ++) printf("%lld ",ans[i].second); puts("");
}

int main() {
    int T = read(),cc = 0;
    while(T --) {
        n = read(),m = read();
        for(int i = 1;i <= n;i ++) A[i]=read(),B[i]=read();
        for(int i = 1;i <= n;i ++) g[i].clear();
        for(int i = 1;i < n;i ++) {
            int x = read(),y = read();
            g[x].push_back(y);
            g[y].push_back(x);
        }
        int rt = get_centroid(1);
        predfs(rt,0);
        sort(g[rt].begin(),g[rt].end(),[](int x,int y) {return sz[x] < sz[y];});
        for(int i = 0;i <= m;i ++) {
            ans[0][i] = ans[1][i] = make_pair(-1,0);
        }
        ans[0][0] = make_pair(0,1);
        ans[1][A[rt]] = make_pair(B[rt],1);
       // cout << rt << endl;
        for(int x : g[rt]) work(x,rt);
        printf("Case %d:\n",++ cc);
        for(int i = 1;i <= m;i ++) {
            pil x = up(ans[0][i],ans[1][i]);
            printf("%lld%c",x.second,i==m?'\n':' ');
        }
        //bl();
    }
}

/**
1
4 5
1 1
2 2
3 2
2 1
1 2
2 3
3 4

3
3 2
1 1
1 1
1 1
1 2
1 3
4 5
1 1
2 2
3 2
2 1
1 2
2 3
3 4
5 10
3 1
2 2
4 4
7 8
5 16
1 2
1 3
2 4
2 5
*/

posted @ 2019-09-17 15:41  DQS  阅读(357)  评论(1编辑  收藏  举报