《2017 Multi-University Training Contest 1》

Add More Zero:

题目让我们求最大的k满足,10 ^ k <= 2 ^ m - 1

即求10 ^ k < 2 ^ m

取对数k * lg10 < m * lg2

k < m * lg2 / lg 10

// Author: levil
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
#include<string>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e7 + 5;
const LL Mod = 1e12;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int main() {
    int m,ca = 0;
    while(cin >> m) {
        int ans = m * 1.0 * log(2) / log(10);
        printf("Case #%d: %d\n",++ca,ans);
    }



 //   system("pause");
    return 0;
}
View Code

Balala Power!:

这个题也挺有意思的,做的时候猜测到了贪心策略就是按整个的数值的大小赋值,代价肯定是最优的,这个结论很好证明 (相邻两个位证明一下)。

但是问题主要是单按给出的数值来赋值会爆long long,所以就要考虑按最高位比较。

这里就有一步非常重要的操作了就是要进位,这样才保证了按最高位往下比较的值肯定满足最大性质。

// Author: levil
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
#include<string>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e7 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

LL mu[N],val[30],all[30];
bool vis[30],tag[30];
struct Node{
    int cc,cnt[N];
}p[30];
bool cmp(Node a,Node b) {
    for(int i = N - 1;i >= 0;--i) {
        if(a.cnt[i] != b.cnt[i]) {
            return a.cnt[i] > b.cnt[i];
        }
    }
    return false;
}
void init() {
    mu[0] = 1;
    for(int i = 1;i < N;++i) mu[i] = mu[i - 1] * 26 % Mod;
}
int main() {
    init();
    int n,ca = 0;
    ios::sync_with_stdio(false);
    while(cin >> n) {
        for(int i = 0;i < 26;++i) {
            for(int j = 0;j < N;++j) {
                p[i].cnt[j] = 0;
            }
            p[i].cc = i;
            vis[i] = val[i] = all[i] = tag[i] = 0;
        }
        for(int i = 1;i <= n;++i) {
            string s;cin >> s;
            int len = s.size();
            for(int j = 0;j < len;++j) {
                int c = s[len - j - 1] - 'a';
                p[c].cnt[j]++;
                all[c] = (all[c] + mu[j]) % Mod;
            }
            if(len > 1) vis[s[0] - 'a'] = 1;
        }
        //进位
        for(int i = 0;i < 26;++i) {
            for(int j = 0;j < N;++j) {
                if(j != N - 1) p[i].cnt[j + 1] += p[i].cnt[j] / 26;
                p[i].cnt[j] %= 26;
            }
        }
        sort(p,p + 26,cmp);
        int col = 25;
        LL ans = 0;

        for(int i = 25;i >= 0;--i) {
            if(vis[p[i].cc] != 1) {
                tag[p[i].cc] = 1;
                break;
            }
        }
        for(int i = 0;i < 26;++i) {
            if(tag[p[i].cc] != 1) {
                val[p[i].cc] = col--;
            }
        }
        for(int i = 0;i < 26;++i) {
            ans = ans + 1LL * val[p[i].cc] * all[p[i].cc] % Mod;
            ans %= Mod;
        }

        printf("Case #%d: %lld\n",++ca,ans);
    }

   // system("pause");
    return 0;
}
/*
5
aabbcc
abc
afaf
zzda
dad
3
aabbcc
abc
afaf
*/
View Code

KazaQ's Socks:

这题比较简单,最后一个位置很显然只有n 和 n - 1两种可能,对于前面的都一直循环。

// Author: levil
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
#include<string>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e7 + 5;
const LL Mod = 1e12;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int main() {
    int caa = 0;
    LL n,k;
    while(cin >> n >> k) {
        printf("Case #%d: ",++caa);
        if(k <= n) printf("%lld\n",k);
        else {
            k -= n;
            LL ma = k % (n - 1);
            LL tmp = k / (n - 1);
            if(ma == 0) {
                printf("%lld\n",tmp % 2 != 0 ? n - 1 : n);
            }
            else {
                printf("%lld\n",ma);
            }
        }
    }
 //   system("pause");
    return 0;
}
View Code

 

Limited Permutation:

这题真的非常优秀,一开始题目一直没读懂。

题意是求:满足所有的L[i],r[i]的排列数量。

贯穿本题目的很重要的一个信息,就是if and only if,他的意思就是说明对于任意pi,a[L + 1]位置和a[r + 1]位置肯定都 < pi.

那么这里就出现了一个很重要的信息,就是区间不会交叉,如果区间交叉。

那么对于a[i],a[j]就会出现不合法的情况,这里可以画一画就能明白。

然后就是本题的一个重要信息,就是对于pi的区间L[i],r[i],以pi为中心,一定存在区间[L[i],pi - 1]和[pi + 1,r[i]]。

也就是子区间必定是这个两个。

证明:假定左区间的最小值位置为x,右区间的最小值位置为y。

因为一共有n个区间,n个数,并且不可相交,所以每个数都肯定会被包含在一个区间内。

那么对于L[i] ~ pi,这段区间的最小值为x,那么很显然由于if and only if的存在,那么它若存在一定是以L[i],pi - 1为边界。

右边同理。所以满足该性质。

有了这一性质,我们就可以递归分治地处理左右子区间,很显然的是pi的值 < 左右区间的最小值。

所以我们每次都放最小的给父节点,然后剩下的排列组合分配。

那么 ans(fa) = ans(Lson) * ans(rson) * C(fa的sz,Lson的sz)。

这题到这还不算完,因为这里的输入究极恶心,必须要读入挂不然速度不够。

然后处理EOF的问题又导致我有些读入挂不行。

而且如果是递归的写法,会出现爆炸的问题,此时需要用

#pragma comment(linker, /STACK:102400000,102400000)来扩大hdu测评的栈区。
或者就不写递归的做法。
// Author: levil
#pragma comment(linker, /STACK:102400000,102400000)
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
#include<string>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e6 + 5;
const int M = 2e6 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e9
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace IO {
    const int MX = 4e7; //1e7占用内存11000kb
    char buf[MX]; int c, sz;
    void begin() {
        c = 0;
        sz = fread(buf, 1, MX, stdin);
    }
    inline bool read(int &t) {
        while(c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
        if(c >= sz) return false;
        bool flag = 0; if(buf[c] == '-') flag = 1, c++;
        for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';
        if(flag) t = -t;
        return true;
    }
}

int L[N],r[N],f[N],inv[N];
map<pii,int> mp;
LL quick_mi(LL a,LL b) {
    LL re = 1;
    while(b) {
        if(b & 1) re = re * a % Mod;
        a = a * a % Mod;
        b >>= 1;
    }
    return re;
}
void init() {
    f[0] = 1;
    for(int i = 1;i < N;++i) f[i] = 1LL * f[i - 1] * i % Mod;
    inv[N - 1] = quick_mi(f[N - 1],Mod - 2);
    for(int i = N - 2;i >= 0;--i) inv[i] = 1LL * inv[i + 1] * (i + 1) % Mod;
}
LL C(int n,int m) {
    return 1LL * f[n] * inv[n - m] % Mod * inv[m] % Mod;
}
LL dfs(int ll,int rr) {
    if(ll > rr) return 1;//这里不能取到等于,因为等于的时候也要判断是否存在这个区间即map[x,x]是否存在
    int pos = mp[pii{ll,rr}];
    if(pos == 0) return 0;
    LL ma = dfs(ll,pos - 1) * dfs(pos + 1,rr) % Mod * C(rr - ll,pos - ll) % Mod;
    return ma;
}
int main() {
    init();
    int n,ca = 0;
    IO::begin();
    while(IO::read(n)) {
        mp.clear();
        for(int i = 1;i <= n;++i) IO::read(L[i]);
        for(int i = 1;i <= n;++i) IO::read(r[i]);
        for(int i = 1;i <= n;++i) {
            mp[pii{L[i],r[i]}] = i;
        }
        LL ans = dfs(1,n);
        printf("Case #%d: %lld\n",++ca,ans);
    }



    //system("pause");
    return 0;
}
View Code

 

Colorful Tree:

又被折磨啦。

一开始想的只需要处理子树,但是后面想了下任意子树之间都可以连成路径,于是去考虑单颜色的贡献,但是总是感觉差一点。

正解:首先肯定是考虑单个颜色的贡献,即统计包含该种颜色的路径有多少条。

但是这个数量还是很难计算,于是正难则反,我们考虑去统计不包含该颜色的路径数。

那么sum[包含] = n * (n - 1)  / 2 - sum[不包含]。这个就是对于该颜色包含的路径方案数。

对于计算不包含的方案数:考虑成连通块的形式,对于这个不包含该颜色的连通块,若数量为sz个,那么可以组成的方案数就是C(sz,2)种。

那么这个连通块的大小怎么算?对于以颜色x为根的子树u,假设已经维护好了它的子树中以所有以x为根的子树大小sz(all)。那么sz[连通块] = sz(u) - sz(all)。

可以画几种例子就能明白。但是我们这样其实只是维护了子树中的方案数,还可能存在跨越根节点1的方案数,所以最后还要遍历一次去计算跨越的方案数。

对于这里的以颜色x为根的子树大小,我用了线段树合并来维护。

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 2e5 + 5;
const int M = 2e6 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e9
#define dbg(ax) cout << "now this num is " << ax << endl;

int n,top = 0,c[N],rt[N],sz[N];
LL tmp[N];
bool vis[N];
vector<int> G[N];
struct Node{int L,r;LL sum;}node[N * 20];
int build(int L,int r,int x) {
    int idx = ++top;
    if(L == r) {
        node[idx].sum = 1;
        return idx;
    }
    int mid = (L + r) >> 1;
    if(mid >= x) node[idx].L = build(L,mid,x);
    else node[idx].r = build(mid + 1,r,x);
    return idx;
}
int Union(int x,int y,int L,int r) {
    if(x == 0) return y;
    if(y == 0) return x;
    if(L == r) {//因为存在下一层的性质,要对最下面做特判
        node[x].sum += node[y].sum;
        return x;
    }
    int mid = (L + r) >> 1;
    node[x].L = Union(node[x].L,node[y].L,L,mid);
    node[x].r = Union(node[x].r,node[y].r,mid + 1,r);
    node[x].sum = node[node[x].L].sum + node[node[x].r].sum;
    return x;
} 
void update(int L,int r,int x,LL val,int idx) {
    if(L == r) {
        node[idx].sum = val;
        return ;
    }
    int mid = (L + r) >> 1;
    if(mid >= x) update(L,mid,x,val,node[idx].L);
    else update(mid + 1,r,x,val,node[idx].r);
}
LL query(int L,int r,int x,int idx) {
    if(idx == 0) return 0;
    if(L == r) return node[idx].sum;
    int mid = (L + r) >> 1;
    if(mid >= x) return query(L,mid,x,node[idx].L);
    else return query(mid + 1,r,x,node[idx].r);
}
void dfs(int u,int fa) {
    sz[u] = 1;
    for(auto v : G[u]) {
        if(v == fa) continue;
        dfs(v,u);
        sz[u] += sz[v];
        LL cnt = sz[v] - query(1,n,c[u],rt[v]);
        if(cnt >= 2) {
            tmp[c[u]] += cnt * (cnt - 1) / 2;
        }
        rt[u] = Union(rt[u],rt[v],1,n);
    }
    update(1,n,c[u],sz[u],rt[u]);
}
int main() {
    int ca = 0;
    while(~scanf("%d",&n)) {
        for(int i = 1;i <= n;++i) G[i].clear(),rt[i] = 0,sz[i] = 0,vis[i] = 0,tmp[i] = 0;
        top = 0;
        for(int i = 1;i <= n;++i) scanf("%d",&c[i]),vis[c[i]] = 1;
        for(int i = 1;i <= n;++i) rt[i] = build(1,n,c[i]);
        for(int i = 1;i < n;++i) {
            int x,y;
            scanf("%d %d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dfs(1,0);
        LL ans = 0,ma = 0;
        for(int i = 1;i <= n;++i) {
            if(vis[i] == 0) continue;
            LL cnt = sz[1] - query(1,n,i,rt[1]);
            //printf("col is %d tmp is %lld query is %lld\n",i,tmp[i],query(1,n,i,rt[1]));
            ans += tmp[i];
            if(cnt >= 2) ans += cnt * (cnt - 1) / 2;
            ma += 1LL * n * (n - 1) / 2;
        }
        ans = ma - ans;
        printf("Case #%d: %lld\n",++ca,ans);
        for(int i = 1;i <= top;++i) node[i].L = node[i].r = 0;
    }

   // system("pause");
    return 0;
}
/*
12
1 2 3 1 2 2 2 1 1 3 3 2
1 2
1 3
2 4
2 5
4 7
5 8
8 11
11 12
3 6
6 9
6 10


*/
View Code

 

Hints of sd0061:

基本想到思路了。显然这里的关键信息就是b[i] + b[j] <= b[k]。

也就是说这里的b数组如果排序之后必定是呈现类似斐波那契数列的增长速度。

在36位之后就超过了1e7,但m最大100,那么显然会有很多重复。

对于查询的操作,显然sort复杂度不够。这里就要用到nth_element。

它在查询到区间第k小之后,还具有一个性质,就是在这段区间中k左边的都小于它,右边的都大于它。

那么我们就可以先对询问排序,然后每次都缩小对下一次查询的区间大小。

但是这里如果正向去缩小,复杂度依旧不够,因为很显然正向开始的询问值都很小,对区间的大小减少没有什么影响。

这就导致了一开始查询的区间都过大,那么我们就考虑反向来,每次都减少较大的区间大小。

PS:这里nth_element一直记成了k要-1,导致一直wa,真是个傻子。还有个细节就是要用%u来输入输出,不然还是wa,因为是unsigned类型,范围和int有点不一样

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e7 + 5;
const int M = 2e6 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e9
#define dbg(ax) cout << "now this num is " << ax << endl;

int n,m;
unsigned solve(unsigned &x,unsigned &y,unsigned &z) {
    unsigned t;
    x ^= x << 16;
    x ^= x >> 5;
    x ^= x << 1;
    t = x;
    x = y;
    y = z;
    z = t ^ x ^ y;
    return z;
}
unsigned a[N],A,B,C;
struct Node{
    int id,val;
    bool operator < (const Node &a)const{
        return val < a.val;
    }
    unsigned ans;
}b[105];
bool cmp(Node a,Node b) {
    return a.id < b.id;
}
int main() {
    int ca = 0;
    while(~scanf("%d %d %u %u %u",&n,&m,&A,&B,&C)) {
        for(int i = 1;i <= m;++i) scanf("%d",&b[i].val),b[i].id = i;
        unsigned x = A, y = B, z = C;
        for(int i = 1;i <= n;++i) a[i] = solve(x,y,z);
        sort(b + 1,b + m + 1);
        nth_element(a + 1,a + b[m].val + 1,a + n + 1);
        b[m].ans = a[b[m].val + 1];
        for(int i = m - 1;i >= 1;--i) {
            if(b[i].val == b[i + 1].val) {
                b[i].ans = b[i + 1].ans;
                continue;
            }
            nth_element(a + 1,a + b[i].val + 1,a + b[i + 1].val + 1 + 1);
            b[i].ans = a[b[i].val + 1];
        }
        sort(b + 1,b + m + 1,cmp);
        printf("Case #%d: ",++ca);
        for(int i = 1;i <= m;++i) printf("%u%c",b[i].ans,i == m ? '\n' : ' ');

    }


   // system("pause");
    return 0;
}
/*
20 10 21121 3552121 12154512
15 4 6 9 8 1 2 5 19 0


*/
View Code

Function:

读题又爆炸了。是计算函数的数量。

第一眼看到就觉得和置换群有关系,我们以样例二为例子。

f[0] = b[f[a[0]]] = b[f[2]]

f[1] = b[f[a[1]]] = b[f[0]]

f[2] = b[f[a[2]]] = b[f[1]]

我们可以发现,0 1 2 - 2 0 1对应形成一个循环群。

所以我们可以知道,函数的每一段一定是一个循环群。

可见如果我们知道了一个循环群中的任意一个数,其他的也就可以推导出来。

所以我们的方案数就是把a中的所有循环群配置好的不同方案数。

因为这里说了两个函数不同是只有一个位置不同即可。

那么对于一个函数中的多个循环群,可以用相同位置中b的循环群。

从上面可见,f[0] = b[f[2]] f[1] = bb[f[2]] f[2] = bbb[f[2]]。

这里可以发现,我们配对的a,b中的循环群长度必须满足,b是a的因子,这样才能保证对应a中的一个数在经过k * len(b)的变换后能回到自己。

而且这个配置的方案数为len(b),因为可以循环着调换位置。

所以方案数就是所有a中的循环群配置方案数的累积。

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 2e6 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e9
#define dbg(ax) cout << "now this num is " << ax << endl;

int n,m,a[N],b[N],ca = 0;
int lp1[N],lp2[N];
bool vis[N];
vector<int> vec1;
int main() {
    while(~scanf("%d %d",&n,&m)) {
        for(int i = 0;i < n;++i) scanf("%d",&a[i]);
        for(int i = 0;i < m;++i) scanf("%d",&b[i]);
        memset(lp1,0,sizeof(lp1));
        memset(lp2,0,sizeof(lp2));
        memset(vis,0,sizeof(vis));
        vec1.clear();
        for(int i = 0;i < n;++i) {
            if(vis[a[i]]) continue;
            int len = 1;
            vis[a[i]] = 1;
            int st = a[i];
            while(vis[a[st]] != 1) {
                vis[a[st]] = 1;
                st = a[st];
                ++len;
            }
            lp1[len]++;
            vec1.push_back(len);
        }
        memset(vis,0,sizeof(vis));
        for(int i = 0;i < m;++i) {
            if(vis[b[i]]) continue;
            int len = 1;
            vis[b[i]] = 1;
            int st = b[i];
            while(vis[b[st]] != 1) {
                vis[b[st]] = 1;
                st = b[st];
                ++len;
            }
            lp2[len]++;
        }
        LL ans = 1;
        for(auto v : vec1) {//a中第i个循环群
            int m = sqrt(v);
            LL ma = 0;
            for(int j = 1;j <= m;++j) {
                if(v % j == 0) {
                    ma = (ma + 1LL * j * lp2[j]) % Mod;//j种摆法
                    if(v / j != j) ma = (ma + 1LL * (v / j) * lp2[v / j]) % Mod;
                }
            }
            ans = ans * ma % Mod;
        }
        printf("Case #%d: %lld\n",++ca,ans);
    }
    system("pause");
    return 0;
}
View Code

I Curse Myself:

这题中间出了亿点点问题。一开始的切入点是生成树加边。

但是仔细看条件发现,每条边最多在一个简单环中,那么显然就存在两种情况:

1:这条边不在环上。

2:这条边在环上。

如果不在环上,那么显然必须要选入。

如果在环上,那么对于每个环,都可以删去一条边保证是一个生成树,注意这里是必须要删去一条(每个环),因为树是不能有环存在。

那么题目就抽象成给定k个环,每个环选一条边,组成最大的k种方案数,那么这就是经典的k路并归问题。

我们优先队列维护即可:这里有一个很重要的优化,没有就会超时。

在merge中,一开始的写法是定义了一个vector然后再每次都调用定义的这个,但是这样会超时,因为我们每次都去申请一个vector,这个复杂度是常数级 * 环的数量。
所以我们可以直接在全局开一个vector,然后每次clear再调用,就能达到很大的优化。
 
对于合并每个环,显然是用tarjan。一开始是这样写的.
void tarjan(int u,int fa) {
    dfn[u] = low[u] = ++tim;
    S.push(u);
    for(auto v : G[u]) {
        if(!dfn[v.to]) {
            tarjan(v.to,u);
            low[u] = min(low[u],low[v.to]);
        }
        else if(v.to != fa) low[u] = min(low[u],dfn[v.to]);
    }
    if(dfn[u] == low[u]) {
        ++cc;
        while(S.top() != u) {
            col[S.top()] = cc;
            S.pop();
        }
        col[S.top()] = cc;
        S.pop();
    }
}
void solve() {
    tarjan(1,0);
    for(int i = 1;i <= n;++i) {
        for(auto v : G[i]) {
            if(v.to < i || col[i] != col[v.to]) continue;
            vec[col[i]].push_back(v.dis);
        }
    }
}
View Code

这就是有向图缩强连通分量的写法,但是这里并不能真正地缩环成功。

可以看一组例子:

在这里我们从7开始dfs,可以发现low[1] = dfn[7],从而导致作为环顶的它不会退栈。所以上诉写法不对。
这样是正确的写法:
void tarjan(int u,int fa) {
    dfn[u] = ++tim;
    for(auto v : G[u]) {
        if(!dfn[v.to]) {
            ffa[v.to] = u;
            tarjan(v.to,u);
        }
        else if(v.to != fa && dfn[v.to] < dfn[u]) {//环尾部
            int j = u;
            vec[++cc].push_back(v.dis);
            while(j != v.to) {
                vec[cc].push_back(d[j][ffa[j]]);
                j = ffa[j];
            }
        } 
    }
}
View Code

这里的思路就是遇到了环尾部就开始退栈。

注意的是上面这种写法可行的条件是因为这是个仙人掌图。

仙人掌图:如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。

如果一条边可以存在在多个环里,那么显然无法缩环,因为我们无法确定地对每条边的归属。

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,int> pii;
const int N = 1e3 + 5;
const int M = 2e3 + 5;
const LL Mod = 4294967296;
#define pi acos(-1)
#define INF 1e9
#define dbg(ax) cout << "now this num is " << ax << endl;

struct Node{int to,dis;};
struct node{
    int v,now;
    friend bool operator < (const node &x,const node &y) {
        return x.v < y.v;
    }
};
vector<Node> G[N];
vector<int> ans,tf;
int dfn[N],low[N],tim,col[N],hav[N],cc = 0,n,m,K,d[N][N],ffa[N];
vector<int> vec[N];
stack<int> S;
void init(int n) {
    memset(dfn,0,sizeof(dfn));
    memset(col,0,sizeof(col));
    for(int i = 1;i <= n;++i) G[i].clear(),vec[i].clear();
    cc = tim = 0;
    while(!S.empty()) S.pop();
}
/*void tarjan(int u,int fa) {
    dfn[u] = low[u] = ++tim;
    S.push(u);
    for(auto v : G[u]) {
        if(!dfn[v.to]) {
            tarjan(v.to,u);
            low[u] = min(low[u],low[v.to]);
        }
        else if(v.to != fa) low[u] = min(low[u],dfn[v.to]);
    }
    if(dfn[u] == low[u]) {
        ++cc;
        while(S.top() != u) {
            col[S.top()] = cc;
            S.pop();
        }
        col[S.top()] = cc;
        S.pop();
    }
}*/
void tarjan(int u,int fa) {
    dfn[u] = ++tim;
    for(auto v : G[u]) {
        if(!dfn[v.to]) {
            ffa[v.to] = u;
            tarjan(v.to,u);
        }
        else if(v.to != fa && dfn[v.to] < dfn[u]) {
            int j = u;
            vec[++cc].push_back(v.dis);
            while(j != v.to) {
                vec[cc].push_back(d[j][ffa[j]]);
                j = ffa[j];
            }
        } 
    }
}
void merge(vector<int> &a,vector<int> b) {
    tf.clear();
    priority_queue<node> q;
    for(int i = 0;i < b.size();i++)
      q.push(node{a[0] + b[i],0});
    while(tf.size() < K && !q.empty()){
        node it = q.top();
        q.pop();
        tf.push_back(it.v);
        if(it.now + 1 < a.size()){
            q.push(node{it.v - a[it.now] + a[it.now + 1],it.now + 1});
        }
    }
    a = tf;
}

void solve() {
    tarjan(1,0);
    /*for(int i = 1;i <= n;++i) {
        for(auto v : G[i]) {
            if(v.to < i || col[i] != col[v.to]) continue;
            vec[col[i]].push_back(v.dis);
        }
    }*/
}
int main() {
    int ca = 0;
    while(~scanf("%d %d",&n,&m)) {
        init(n);
        LL all = 0;
        for(int i = 1;i <= m;++i) {
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            G[x].push_back(Node{y,z});
            G[y].push_back(Node{x,z});
            d[x][y] = d[y][x] = z;
            all += z;
        }
        scanf("%d",&K);
        solve();
        ans.clear();
        ans.push_back(0);
        for(int i = 1;i <= cc;++i) {
            if(vec[i].size() != 0) merge(ans,vec[i]);
        }
        LL ma = 0;
        for(int i = 0;i < ans.size();++i) ma = (ma + 1LL * (i + 1) * (all - ans[i]) % Mod) % Mod;
        printf("Case #%d: %lld\n",++ca,ma);
    }

    //system("pause");
    return 0;
}
/*
12 14
1 2 1000000000
1 3 1000000000
2 4 1000000000
3 4 1000000000
1 5 1000000000
5 6 1000000000
5 8 1000000000
6 7 1000000000
7 8 1000000000
7 9 1000000000
9 10 1000000000
10 11 1000000000
11 12 1000000000
9 12 1000000000
40

10 10
1 2 2
1 3 4 
1 4 3
1 5 6
1 6 22
1 7 31
1 8 12
1 9 12
9 10 24
8 10 7
40


*/
View Code

 

posted @ 2021-07-08 17:10  levill  阅读(29)  评论(0编辑  收藏  举报