The Preliminary Contest for ICPC Asia Shenyang 2019

传送门

B. Dudu's maze

题意:
是什么鬼东西???我读题可以读半小时QAQ
给出一张无向图,一个人在里面收集糖果,每个点都有一个糖果,特殊点除外。当他第一次进入特殊点时,会随机往一条边走;之后再进入特殊点时,直接退出。
问最后期望获得的糖果数为多少。

思路:

  • 注意到这个人肯定会贪心走的,即是每次到一个连通块,肯定要把里面的糖果都拿完,然后再选一个随机走。
  • 不管往哪个方向,还是类似地贪心走,将那个连通块走完。
  • 所以就两次\(dfs\)好了,第一次\(dfs\)首先求出第一个连通块,然后标出所有的特殊点;第二次\(dfs\)以特殊点为起点搜连通块。

大体思路就是这样,注意一些细节:就是可能以某个特殊点为起点的时候,会多次到达同一个连通块,但答案不能重复计算。这里我的解决办法就是用了一个类似于时间戳一样的东西来进行标记,同一个连通块的属于同一个时间,并且用一个数组记录时间的答案。
详见代码:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005, M = 200005;

int n, m, k;
int T;
bool chk[N];

struct Edge{
    int v, next;
}e[M << 1];
int head[N], tot;
void adde(int u, int v) {
    e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}

bool vis[N];
vector <int> vec;
int dfs(int u) {
    int sz = 1; vis[u] = 1;
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(vis[v] || chk[v]) {
            if(chk[v]) vec.push_back(v);
            continue;
        }
        sz += dfs(v);
    }
    return sz;
}

int dfn;
int D[N], ans[N], du[N];
int dfs2(int u) {
    int sz = (chk[u] ? 0 : 1);
    D[u] = dfn; vis[u] = 1;
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(chk[v]) continue;
        if(vis[v]) {
            if(chk[u] && !chk[v]) sz += ans[D[v]];
            continue;
        }
        if(chk[u]) {
            ans[++dfn] = dfs2(v);
            sz += ans[dfn];
        }
        else sz += dfs2(v);
    }
    return sz;
}

int main() {
//    freopen("input.in", "r", stdin);
    cin >> T;
    while(T--) {
        cin >> n >> m >> k;
        for(int i = 0; i <= n; i++) {
            du[i] = ans[i] = D[i] = 0;
            head[i] = -1; chk[i] = vis[i] = false;
        }
        vec.clear();
        tot = 0;
        for(int i = 1; i <= m; i++) {
            int u, v; scanf("%d%d", &u, &v);
            adde(u, v); adde(v, u);
            ++du[u], ++du[v];
        }
        for(int i = 1; i <= k; i++) {
            int x; scanf("%d", &x);
            chk[x] = true;
        }
        int res = dfs(1), tmp;
        double ANS = 0;
        dfn = 0;
        for(auto it : vec) {
            int sz = dfs2(it);
            ANS = max(ANS, res + 1.0 * sz / du[it]);
        }
        printf("%.8f\n", ANS);
    }
    return 0;
}

C. Dawn-K's water

简单\(dp\)

Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e4+5,MAXM = 2e6+5,MOD = 998244353,INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;

int n,m,p[1005],c[1005];
ll f[10005];
int main(){
    ios::sync_with_stdio(false);
    //freopen("../A.in","r",stdin);
    //freopen("../A.out","w",stdout);
    while(cin>>n>>m){
        ll ans=1000000000000000;
        int b=-1,up=10000,sum=0;
        for(int i=1;i<=n;i++){
            cin>>p[i]>>c[i];
            sum+=c[i];
        }
        //up=min(up,sum);
        f[0]=0;
        for(int i=1;i<=up;i++)f[i]=1000000000000000;
        for(int i=1;i<=n;i++){
            for(int j=c[i];j<=up;j++){
                f[j] = min(f[j],f[j-c[i]] + p[i]);
            }
        }
        for(int i=m;i<=up;i++){
            if(f[i]<=ans){
                ans=f[i];
                b=i;
            }
        }
        //assert(ans>=1&&ans<=1000000000);
        assert(b!=-1);
        cout<<ans<<' '<<b<<'\n';
    }
    return 0;
}

D. Fish eating fruit

题意:
这什么题意啊,欺负我英语不好么QAQ

思路:
按照模\(3\)余数分类,然后换根\(dp\)就行了。
代码队友写的,可能有点迷(我反正没怎么看懂)

Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e4+5,MAXM = 2e4+5,MOD = 1e9+7,INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;
struct Edge{
    int v,w,next;
}e[MAXM];
int n,a,b,c,head[MAXN],cnt,f1[MAXN][3],g1[MAXN][3],ans[3],f2[MAXN][3],g2[MAXN][3];
inline void init(){
    cnt=0;
    for(int i=1;i<=n;i++)head[i]=0;
    for(int i=1;i<=n;i++){
        for(int r=0;r<3;r++)f1[i][r]=g1[i][r]=0;
    }
}
inline void addEdge(int u,int v,int w){
    e[++cnt] = {v,w,head[u]};head[u]=cnt;
}
inline void add(int &x,int y){
    x+=y;
    if(x>=MOD)x-=MOD;
}
void dfs(int u,int p){
    g1[u][0]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==p)continue;
        dfs(v,u);
        for(int r=0;r<3;r++){
            add(f1[u][(e[i].w+r)%3],(f1[v][r] + (ll)g1[v][r]*e[i].w%MOD)%MOD);
            add(g1[u][(e[i].w+r)%3],g1[v][r]);
        }
    }
}
void solve(int u,int p){
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==p)continue;
        for(int r=0;r<3;r++){
            int t = (r+e[i].w)%3;
            int t2 = ((r-e[i].w)%3+3)%3;
            add(f2[v][t],(f2[u][r]-f1[v][t2]-(ll)g1[v][t2]*e[i].w + (ll)e[i].w*(g2[u][r] - g1[v][t2])%MOD+MOD)%MOD);
            add(g2[v][t],(g2[u][r]-g1[v][t2]+MOD)%MOD);
        }
        solve(v,u);
    }
}
int main(){
    ios::sync_with_stdio(false);
    //freopen("../A.in","r",stdin);
    //freopen("../A.out","w",stdout);
    while(cin>>n){
        init();
        for(int i=1;i<n;i++){
            cin>>a>>b>>c;
            a++;b++;
            addEdge(a,b,c);addEdge(b,a,c);
        }
        dfs(1,0);
        for(int i=1;i<=n;i++){
            for(int r=0;r<=2;r++){
                g2[i][r] =g1[i][r];
                f2[i][r] =f1[i][r];
            }
        }
        solve(1,0);
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++){
            for(int r=0;r<3;r++)add(ans[r],f2[i][r]);
        }
        cout<<ans[0]<<' '<<ans[1]<<' '<<ans[2]<<'\n';
    }
    return 0;
}

F. Honk's pool

考虑先取最大的数,可以二分一个最后的最大值,然后随便搞搞,把取完过后的数组求出来。
之后贪心依次来放就行。
总的来说,就是将取和放独立开,这两个是独立的,并不影响。
为什么?
我也不会证= =。

Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

const int N = 500005;

ll k, n;
ll a[N], sum[N];

bool chk(int x) {
    int p = lower_bound(a + 1, a + n + 1, x) - a;
    int num = n - p + 1;
    ll S = sum[p] - 1ll * x * num;
    return S <= k;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    while(cin >> n >> k) {
        for(int i = 1; i <= n; i++) cin >> a[i];
        sort(a + 1, a + n + 1); sum[n + 1] = 0;
        for(int i = n; i >= 1; i--) sum[i] = sum[i + 1] + a[i];
        int l = 0, r = INF, mid;
        while(l < r) {
            mid = (l + r) >> 1;
            if(chk(mid)) r = mid;
            else l = mid + 1;
        }
        int v = r;
        int p = lower_bound(a + 1, a + n + 1, v) - a;
        int num = n - p + 1;
        ll S = sum[p] - 1ll * v * num;
        k -= S;
        for(int i = p; i <= n; i++) a[i] = v;
        for(int i = p; i <= min(n, p + k - 1); i++) {
            if(a[i]) { --a[i]; ++S; }
        }
    //    for(int i = 1; i <= n; i++) cout << a[i] << ' ';
    //    cout << '\n';
        int cnt = 1, f = 1;
        for(int i = 1; i <= n; i++) {
            if(a[i] >= v) break;
            ll tmp = 1ll * cnt * (a[i + 1] - a[i]);
            if(S <= tmp) {
                int now = S / cnt;
                cout << v - (a[i] + now) << '\n';
                f = 0; break;
            }
            S -= 1ll * cnt * (a[i + 1] - a[i]);
            ++cnt;
        }
        if(f == 0) continue;
        if(S % n) cout << 1 << '\n';
        else cout << 0 << '\n';
    }
    return 0;
}
/*
3 10000000
1 2 3
*/

H. Texas hold'em Poker

题意:
不懂。

思路:
模拟。

Code
#include<cstdio>
#include<cmath>
#include<cctype>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define REP(r,x,y) for(register int r=(x); r<y; r++)
#ifdef sahdsg
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...) (void)0
#endif
template <class T>
inline void read(T&x) {
	static int si=1; static char ch; x=0;
	do ch=getchar(); while(!isdigit(ch) && ch!='-');
	if(ch=='-') si=-1, ch=getchar();
	while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} x*=si;
}
template <class T, class... A> inline void read(T&t, A&...a){read(t); read(a...);}
#define MAXN 100007
char nm[MAXN][17];
struct node {
	int id;
	int s[5];
	int kind;
	int v,v2,v3;
};
node p[MAXN];
inline void readpai(int x) {
	char ch;
	REP(i,0,5) {
		do ch=getchar(); while(ch<=' ');
#define L p[x].s[i]
		switch(ch) {
			case 'A':
				L=1; break;
			case '1':
				getchar();
				L=10; break;
			case 'J':
				L=11; break;
			case 'Q':
				L=12; break;
			case 'K':
				L=13; break;
			default:
				L=ch-'0'; break;
		}
#undef L
	}
	sort(p[x].s,p[x].s+5);
}
inline void processpai(int x) {
	const int *X=p[x].s; int &K=p[x].kind, &V=p[x].v, &V2=p[x].v2, &V3=p[x].v3;
	//7 XJQKA
	const int RS[]={1,10,11,12,13};
	if(memcmp(X,RS,sizeof RS)==0) {K=7;V=0;return;}
	//6 12345
	if(X[1]-X[0]==1 && X[2]-X[1]==1 && X[3]-X[2]==1 && X[4]-X[3]==1) {K=6, V=X[4]; return;}
	//5 AAAA
	if(X[1]==X[2] && X[2]==X[3]) {
		if(X[0]==X[1]) {K=5, V=X[1], V2=X[4]; return;}
		if(X[3]==X[4]) {K=5, V=X[1], V2=X[0]; return;}
	}
	//4 AAABB
	if(X[0]==X[1] && X[1]==X[2]   && X[3]==X[4]) {K=4, V=X[0], V2=X[3]; return;}
	if(X[0]==X[1] &&   X[2]==X[3] && X[3]==X[4]) {K=4, V=X[2], V2=X[0]; return;}
	//3 AAABC
	if(X[0]==X[1] && X[1]==X[2]) {K=3, V=X[0], V2=X[3]+X[4]; return;}
	if(X[1]==X[2] && X[2]==X[3]) {K=3, V=X[1], V2=X[0]+X[4]; return;}
	if(X[2]==X[3] && X[3]==X[4]) {K=3, V=X[2], V2=X[0]+X[1]; return;}
	//2 AABBC
	if(X[0]==X[1] && X[2]==X[3]) {K=2, V=X[2], V2=X[0], V3=X[4]; return;}
	if(X[0]==X[1] && X[3]==X[4]) {K=2, V=X[3], V2=X[0], V3=X[2]; return;}
	if(X[1]==X[2] && X[3]==X[4]) {K=2, V=X[3], V2=X[1], V3=X[0]; return;}
	//1 AABCD
	if(X[0]==X[1]) {K=1, V=X[0], V2=X[2]+X[3]+X[4]; return;}
	if(X[1]==X[2]) {K=1, V=X[1], V2=X[0]+X[3]+X[4]; return;}
	if(X[2]==X[3]) {K=1, V=X[2], V2=X[0]+X[1]+X[4]; return;}
	if(X[3]==X[4]) {K=1, V=X[3], V2=X[0]+X[1]+X[2]; return;}
	//0 
	{K=0, V=X[0]+X[1]+X[2]+X[3]+X[4];return;}
}
bool nmcmp(const node&l, const node&r) {
	return strcmp(nm[l.id],nm[r.id])<0;
}
bool cmp(const node&l, const node&r) {
	const bool T=false;
	if(l.kind<r.kind) return T;
	if(l.kind==r.kind) {
		switch(l.kind) {
			case 0:
			case 6:
			case 7:
				if(l.v<r.v) return T;
				if(l.v==r.v) return nmcmp(l,r);
				else return !T;
			case 1:
			case 3:
			case 4:
			case 5:
				if(l.v<r.v) return T;
				if(l.v==r.v) {
					if(l.v2<r.v2) return T;
					if(l.v2==r.v2) {
						return nmcmp(l,r);
					} else return !T;
				} else return !T;
			case 2:
				if(l.v<r.v) return T;
				if(l.v==r.v) {
					if(l.v2<r.v2) return T;
					if(l.v2==r.v2) {
						if(l.v3<r.v3) return T;
						if(l.v3==r.v3) {
							return nmcmp(l,r);
						} else return !T;
					} else return !T;
				} else return !T;
		}
	}
	return !T;

}
int main() {
	int n;
	while(~scanf("%d", &n)) {
		REP(i,0,n) {
			scanf("%s", nm[i]);
			p[i].id=i;
			readpai(i);
			processpai(i);
		}
		sort(p,p+n,cmp);
		REP(i,0,n) {
			puts(nm[p[i].id]);
		}
	}
	return 0;
}

J. Ghh Matin

题意:
给出\(n\)个点,现在随机生成边,要求每个点只有一条出边和一条入边(形成若干个环)。同时给出一个\(x\)
问最后生成的最长环不超过\(x\)的概率。
注意\(2x\geq n\)

思路:
首先可以知道,对于\(n\)个点,随机形成若干个环的情况总数为\(n!\)。为什么?

因为最后的图中肯定有\(n\)条边,我们现在开始一条一条加边,第一次选择两个出度为\(0\)和入度为\(0\)的点将其相连,易知一开始有\(n\)总选择,后面依次有\(n-1,n-2,\cdots,1\)种选择。

注意条件\(2x\geq n\),那么说明图中长度大于\(x\)的环最多只有一个,那么我们考虑反面,就枚举所有不合法的情况,此时就很好计算:
假设现在考虑长度为\(m\)的环,那么首先会从\(n\)个里面选择\(m\)个,之后会随机成环,此时只有\((m-1)!\)种情况,相当于固定一个点,其它点随机排列(如果直接全排列,可能会有重复的)。之后剩下的点随机成环,有\((n-m)!\)种情况。
那么反面情况的总概率为:

\[\sum_{m=x+1}^n{n\choose m}(m-1)!(n-m)!/n! \]

之后随便化一化就出来了。
正难则反。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 5, MOD = 1e9 + 7;

int n, x;
int p[N];

ll qpow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}

void init() {
    for(int i = 1; i < N; i++) p[i] = qpow(i, MOD - 2);
}

void run() {
    cin >> n >> x;
    int ans = 0;
    for(int i = x + 1; i <= n; i++) ans = (ans + p[i]) % MOD;
    ans = (1 - ans + MOD) % MOD;
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    init();
    int T; cin >> T;
    while(T--) run();
    return 0;
}

posted @ 2019-09-18 22:20  heyuhhh  阅读(296)  评论(0编辑  收藏  举报