• 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;
    }
    
  • 相关阅读:
    Elasticsearch5.3 学习(一):安装、Yii2.0 下载es扩展
    lnmp 环境require(): open_basedir restriction in effect 错误
    Ueditor编辑器图片上传到万象优图
    Linux curl 模拟form表单提交信息和文件
    CP936 转换成 UTF-8
    wamp 两个不同的php.ini
    PHP浮点数运算精度造成的,订单金额支付经常少1分的问题
    进制相关:存储与转换
    Python的数据类型
    pycharm+PyQt5 开发配置
  • 原文地址:https://www.cnblogs.com/s-r-f/p/13581431.html
Copyright © 2020-2023  润新知