zoukankan      html  css  js  c++  java
  • Atcoder Dwango Programming Contest 6th 题解

    (A) (Falling) (Asleep)

    题目链接

    (description:)

    给你一个歌单(,) (n)首歌的歌名(s_i)和播放持续时间(t_i) (,)

    读入一个歌名(st,)要求在歌单中算出在这首曲子之后所有曲子的播放时间总和(.)

    (solution:)

    暴力即可(.) 时间复杂度 (O(n + sum|s_i|))

    (code:)

    #include <bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x = 0; char c = getchar();
        while (!isdigit(c)) c = getchar();
        while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
        return x;
    }
    template <typename T> void read(T &x){
    	x = 0; int f = 1; char ch = getchar();
    	while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    	while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    	x *= f;
    }
    inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    int n; string st,s[500]; int t[500];
    int main(){
    	cin >> n;
    	for (int i = 1; i<= n; ++i) cin >> s[i] >> t[i];
    	cin >> st; 
    	int id = -1; long long ans = 0;
    	for (int i = 1; i <= n; ++i) if (s[i] == st) id = i;
    	for (int i = id+1; i <= n; ++i) ans += t[i];
    	cout << ans << endl;
        return 0;
    }
    

    (B) (Fusing) (Slimes)

    题目链接

    (description :)

    (n(n≤10^5))块石子(,)每个石子有其初始坐标(x_i.)

    每次你会从中随机选出不在最右边的一堆(,)将这一堆移动到 它右边最靠近它的一堆石子的位置(,)并把这两堆石子合并为一堆(.)

    求出所有情况下(,)石子移动距离的总和 (,) 即期望乘以 ((n-1)!) (.)

    答案对(P = 1e9 + 7) 取模(.)

    (solution :)

    考虑一段距离(d_i = x_{i+1} - x_i)

    定义 $dp[n] = $ 一段距离之前有(n)块石子(,)把这(n)块石子移动过去之后(,)这段距离被经过的期望次数

    不难得出 (dp[i] = dp[i-1] + 1/i)

    然后就可以直接计算出每一段距离对答案的贡献了(.)

    时间复杂度(O(n).)

    (code :)

    #include <bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x = 0; char c = getchar();
        while (!isdigit(c)) c = getchar();
        while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
        return x;
    }
    template <typename T> void read(T &x){
    	x = 0; int f = 1; char ch = getchar();
    	while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    	while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    	x *= f;
    }
    inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    const int P = 1e9 + 7,N = 500050;
    int n,fac[N],inv[N];
    int x[N];
    int f[N];
    long long ans,now;
    int main(){
    	int i;
    	read(n);
    	for (i = 1; i <= n; ++i) read(x[i]);
    	for (fac[0] = i = 1; i <= n; ++i) fac[i] = 1ll*fac[i-1]*i%P;
    	for (inv[0] = inv[1] = 1,i = 2; i <= n; ++i) inv[i] = 1ll*inv[P%i]*(P-P/i)%P;
    	for (i = 1; i <= n; ++i){
    		int prob; prob = inv[i];
    		f[i] = 1ll*prob*(f[i-1]+1) % P;
    		f[i] += 1ll*(P+1-prob)*f[i-1] % P;
    		f[i] %= P;
    	}
    	ans = 0;
    	for (i = 1; i <= n-1; ++i){
    		int dist = x[i+1] - x[i];
    		ans = (ans + 1ll*dist*f[i]%P)%P;
    	} 
    	cout << 1ll*fac[n-1]*ans%P << endl;
        return 0;
    

    (C) (Cookie) (Distribution)

    题目链接

    $description : $

    (n) 个人 (,)(k) 天中你要给他们发糖果 (.)

    读入 (a_1 .. a_k,) 其中 (a_i) 表示第(i)天会从(n)个人当中均匀随机选出 (a_i) 个人 (,) 并给他们每人发一颗糖 (.)

    (c_i)为第(i)个人发到的糖果个数(,)(Pi c_i)的期望(.)

    (n<=10^3,k<=20)

    (solution:)

    (prod c_i) 相当于求 从 (n) 个人中每个人手里选一颗糖的方案数

    考虑枚举 (x_i) 表示从第 (i) 个人手里选的糖果是从哪一天来的 (.)

    (x_i) 是什么并不重要(,)重要的是(cnt2_i :) 表示有多少(x_j) (=) (i)

    这种选法对答案的贡献是

    [large prod^{k}_{i=1} C^{a_k-cnt2_k}_{n-cnt2_k} imes prod_{i=1}^{k} (cnt2_i!)^{-1} imes n! ]

    然后用一个(O(nk^2) dp)就可以解决这个问题了(.)

    (code :)

    #include <bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x = 0; char c = getchar();
        while (!isdigit(c)) c = getchar();
        while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
        return x;
    }
    template <typename T> void read(T &x){
    	x = 0; int f = 1; char ch = getchar();
    	while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    	while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    	x *= f;
    }
    inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    
    const int N = 1050,K = 20 + 5,P = 1e9 + 7;
    int n,k,a[K];
    int fac[N],nfac[N],inv[N];
    inline int C(int n,int m){ return (n<m||n<0||m<0) ? 0 : 1ll*fac[n]*nfac[m]%P*nfac[n-m]%P; }
    int t[K][N];
    int dp[K][N];
    int main(){
    	int i,j,e;
    	read(n),read(k); for (i = 1; i <= k; ++i) read(a[i]);
    	inv[0] = fac[0] = nfac[0] = inv[1] = fac[1] = nfac[1] = 1;
    	for (i = 2; i <= n; ++i){
    		fac[i] = 1ll*fac[i-1]*i%P;
    		inv[i] = 1ll*(P-P/i)*inv[P%i]%P;
    		nfac[i] = 1ll*nfac[i-1]*inv[i]%P;
    	} 
    	for (i = 1; i <= k; ++i)
    	for (j = 0; j <= n; ++j) t[i][j] = 1ll*C(n-j,a[i]-j)*nfac[j]%P;
    	dp[0][0] = 1;
    	for (i = 1; i <= k; ++i)
    	for (j = 0; j <= n; ++j){
    		dp[i][j] = 0;
    		for (e = 0; e <= j; ++e) dp[i][j] = (dp[i][j] + 1ll * t[i][e] * dp[i-1][j-e]) % P;
    	}
    	int ans = 1ll * dp[k][n] * fac[n] % P; 
        cout << ans << endl;
    	return 0;
    }
    

    (D) (Arrangement)

    题目链接

    (description :)

    给你一张图(G,) (G)(n)个点(,) (n*(n-2)) 条有向边(.)

    (G)的补图是一个所有点出度(=1) (() 可能有自环 ()) 的有向图(.)

    求出一条字典序最小的哈密尔顿路径(,)如果不存在则输出(-1.)

    (n<=10^5)

    (solution :)

    首先如果规模足够小((n<=8))就可以直接暴力(O(n!))

    然后我们考虑对于(n)更大的情况来处理答案(.)

    记$cnt_i = $ 当前的图中(,) 有多少(a_j = i)

    如果存在一个点(p)满足(cnt_p = n-1)那么我们必须选这个点作为路径的第一个点(.)

    如果不存在这样的一个点(,)我们就可以选择当前的点当中编号最小的点(.)

    然后问题就变成了一个规模为(n-1)的子问题(,)只是多了一个开头不能为某个数的限制(.)

    那么我们就可以写一个支持 查询(cnt)最大值(,) (cnt)单点修改 和 查询当前点集的编号最小者(,) 删除一个点的数据结构(,) 再写一个(O(n!))的暴力即可(.)

    复杂度(O(8! +nlogn))

    (code:)

    #include <bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x = 0; char c = getchar();
        while (!isdigit(c)) c = getchar();
        while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
        return x;
    }
    template <typename T> void read(T &x){
    	x = 0; int f = 1; char ch = getchar();
    	while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    	while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    	x *= f;
    }
    inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    const int N = 100050;
    int n,a[N];
    
    namespace subtask0{
    	int ans[100],vis[100]; bool ok;
    	int p[100];
    	inline void chk(){
    		for (int i = 2; i <= n; ++i) if (a[ans[i-1]] == ans[i]) return;
    		ok = 1; for (int i = 1; i <= n; ++i) p[i] = ans[i];
    		return; 
    	}
    	inline void dfs(int dep){
    		if (ok) return;
    		if (dep > n){ chk(); return; }
    		for (int i = 1; i <= n; ++i) if (!vis[i]){
    			vis[i] = 1,ans[dep] = i;
    			dfs(dep+1);
    			vis[i] = 0;
    		}
    	}
    	inline void solve(){
    		ok = 0; memset(vis,0,sizeof(vis)); memset(ans,0,sizeof(ans)); dfs(1);
    		if (!ok){ puts("-1"); return; }
    		for (int i = 1; i <= n; ++i) cout << p[i] << ((i<n) ? (' '):('
    '));
    	}
    }
    
    int mx[N<<2],mxi[N<<2],data[N<<2],cnt[N];
    inline void Build(int o,int l,int r){
    	if (l^r){
    		int mid = l+r>>1; Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
    		mx[o] = max(mx[o<<1],mx[o<<1|1]); data[o] = min(data[o<<1],data[o<<1|1]);
    		mxi[o] = mxi[ (mx[o<<1]>mx[o<<1|1]) ? (o<<1) : (o<<1|1) ];
    		return;
    	}
    	mx[o] = cnt[l]; data[o] = (mx[o] >= 0) ? (l) : (n+1); mxi[o] = l;
    }
    inline int Ask(int o,int l,int r,int p){
    	if (l==r) return mx[o];
    	int mid = l+r>>1; return (p<=mid) ? Ask(o<<1,l,mid,p) : Ask(o<<1|1,mid+1,r,p);
    } 
    inline void Add(int o,int l,int r,int p,int v){
    	if (l==r){ mx[o]=v; data[o] = (mx[o] >= 0) ? (l) : (n+1); return; }
    	int mid = l+r>>1; if (p<=mid) Add(o<<1,l,mid,p,v); else Add(o<<1|1,mid+1,r,p,v);
    	mx[o] = max(mx[o<<1],mx[o<<1|1]); data[o] = min(data[o<<1],data[o<<1|1]);
    	mxi[o] = mxi[ (mx[o<<1]>mx[o<<1|1]) ? (o<<1) : (o<<1|1) ];
    }
    inline int Nowv(){ return data[1]; }
    inline int Query(int pos){ if (pos==0) return -1; return Ask(1,1,n,pos); }
    inline void Modify(int x,int v){ cnt[x] = v; Add(1,1,n,x,v); }
    
    
    int ans[N];
    int vis[N],nowc[N],lc;
    int qwq;
    inline void chk(){
    	for (int i = qwq; i <= n; ++i) if (ans[i]==a[ans[i-1]]) return;
    	for (int i = 1; i <= n; ++i) write(ans[i]),putchar((i<n)?(' '):('
    '));
    	exit(0);
    }
    inline void dfs(int dep){
    	if (dep>n){ chk(); return; }
    	for (int i = 1; i <= lc; ++i) if (!vis[i]){
    		vis[i] = 1;
    		ans[dep] = nowc[i];
    		dfs(dep+1);
    		vis[i] = 0;
    	}
    }
    inline void solve_force(int l,int r){
    	qwq = l;
    	for (int i = 1; i <= n; ++i) if (Query(i) >= 0) nowc[++lc] = i,vis[lc] = 0;
    	dfs(l);
    }
    inline bool unproperty(){
    	for (int i = 1; i <= n; ++i) cnt[i] = 0;
    	for (int i = 1; i <= n; ++i) ++cnt[ans[i]];
    	for (int i = 1; i <= n; ++i) if (cnt[i] != 1) return 1;
    	for (int i = 2; i <= n; ++i) if (ans[i] == a[ans[i-1]]) return 1;
    	return 0;
    }
    int main(){
    	int i,banp,ret,siz,p;
    	int pp,val;
    	read(n);
    	for (i = 1; i <= n; ++i) read(a[i]);
    	for (i = 1; i <= n; ++i) if (a[i]==i) a[i]=0;
    	for (i = 1; i <= n; ++i) ++cnt[a[i]];
    //	if (n==2){ puts("-1"); return 0; }
    	if (n<=8){ subtask0::solve(); return 0; }
    	
    	Build(1,1,n);
    	for (banp = 0,siz = n,i = 1; i <= n; ++i,--siz){
    		if (siz <= 6){
    			solve_force(i,n); 
    		}
    		banp = a[ans[i-1]];
    		if (banp != 0 && (ret=Query(banp)) >= 0){
    			Modify(banp,-1);
    			if (mx[1] == siz-1){
    				p = mxi[1];
    				ans[i] = p;
    				Modify(ans[i],-1);
    			}
    			else{
    				p = data[1];
    				ans[i] = p;
    				Modify(ans[i],-1);
    			}
    			pp = a[ans[i]];
    			if ((val=Query(pp))>=0){ --val; Modify(pp,val); }
    			Modify(banp,ret);
    		}
    		else{
    			if (mx[1] == siz-1){
    				p = mxi[1];
    				ans[i] = p;
    				Modify(ans[i],-1);
    			}
    			else{
    				p = data[1];
    				ans[i] = p;
    				Modify(ans[i],-1);
    			}
    			pp = a[ans[i]];
    			if ((val=Query(pp))>=0){ --val; Modify(pp,val); }
    		}
    	}
    	if (unproperty()){ puts("-1"); return 0; }
    	for (i = 1; i <= n; ++i) write(ans[i]),putchar((i<n)?(' '):('
    '));
    }
    

    (E) (Span) (Covering)

    题目链接

    (description:)

    (n)条长度为(l_i)的线段(.)

    你要把这些线段放到一个长为(X)的坐标轴上 (() (n <=100) (X <= 500) ())

    并且满足一个条件(:) 不能覆盖到外面(,)也不能有地方没有被任何一条线段覆盖到(.)

    求方案数(.)

    (solution:)

    首先把(l_i)从大到小排序(.)

    考虑一个(dp:)

    $f[i][j][k] = $ 前(i)个区间组成了(j)个相邻的开区间(,)其总长度为(k)的方案数(.)

    转移有三种(:)

    (1.) 新开一个区间(;(j->j+1))

    (2.) 把当前的某一个区间和我新加进去的区间合并为一个区间(;(j->j))

    (3.) 把两个相邻的区间通过我新加进去的区间合并成一个区间(.(j->j-1))

    其中第(1)种转移的长度是确定的(,)(k + len)

    (2) (3)种转移的长度是需要枚举的(.)

    看起来复杂度是(O(n^2X^2),)但是在枚举到(i)的时候(,)只有(k*l_i <= j)的状态是有用的(.)

    所以复杂度为(O(???) 能过 ()) (() 好像是 (O(nX^2)?) ())

    (code:)

    #include <bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x = 0; char c = getchar();
        while (!isdigit(c)) c = getchar();
        while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
        return x;
    }
    template <typename T> void read(T &x){
    	x = 0; int f = 1; char ch = getchar();
    	while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    	while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    	x *= f;
    }
    inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    const int N = 100 + 5,M = 1050,P = 1e9 + 7;
    int n,m;
    
    int f[N][M],g[N][M];
    inline void upd(int &x,int y){ x+=y; x>=P?x-=P:0; x<0?x+=P:0; }
    inline void DP(int L){
    	int i,j,k;
    //	cout <<"DP " << endl;
    //	for (i = 1; i <= n; ++i,cout << endl)
    //	for (j = 1; j <= m; ++j) cout <<f[i][j] << ' ';
    	
    	for (i = 1; i <= n; ++i)
    	for (j = 1; j <= m; ++j) g[i][j] = f[i][j],f[i][j] = 0;
    	for (i = 1; i <= n; ++i)
    	for (j = 1; j <= m; ++j) if (g[i][j]>0){
    		upd(f[i+1][j+L],1ll*(i+1)*g[i][j]%P);
    		upd(f[i][j],1ll*g[i][j]*(P+j-i*(L-1))%P);
    		for (k = 1; k < L; ++k) upd(f[i][j+k],2ll*g[i][j]%P*i%P);
    		for (k = 0; k < L-1; ++k) upd(f[i-1][j+k],1ll*g[i][j]*(L-k-1)%P*(i-1)%P);
    	}
    }
    int a[N];
    int main(){
    	int i,j;
    	int rm;
    	read(n),read(m); for (i = 1; i <= n; ++i) read(a[i]),++a[i]; rm = m; m += 1;
    	sort(a+1,a+n+1); reverse(a+1,a+n+1);
    	memset(f,0,sizeof(f)); f[1][a[1]] = 1;
    	for (i = 2; i <= n; ++i) DP(a[i]);
    	
    //	cout <<"DP " << endl;
    //	for (i = 1; i <= n; ++i,cout << endl)
    //	for (j = 1; j <= m; ++j) cout <<f[i][j] << ' ';
    	int ans = 0;
    	for (i = 1; i <= 1; ++i) ans += f[i][rm+i],ans %= P;
        cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    MFC Windows 程序设计>WinMain 简单Windows程序 命令行编译
    AT3949 [AGC022D] Shopping 题解
    CF643D Bearish Fanpages 题解
    CF643C Levels and Regions 题解
    CF241E Flights 题解
    CF671C Ultimate Weirdness of an Array 题解
    CF1592F Alice and Recoloring 题解
    GYM 102452E 题解
    CF494C Helping People 题解
    P5556 圣剑护符
  • 原文地址:https://www.cnblogs.com/s-r-f/p/13581431.html
Copyright © 2011-2022 走看看