Codeforces Round #302 (Div. 1) 训练
链接:
http://codeforces.com/contest/543
过程:
惨淡的只做出了A和C
题解:
A
题解:
简单的一道题
我们用$dp[i][j]$表示当前考虑到前num个人(这个另外枚举),当前写了$i$行代码,当前的bug数量为$j$的方案数
然后转移就是$dp[i][j]=\sum {dp[i-1][j-a[num]]}$
Code:
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=,f=;char c=getchar();
while(c<'' || c>''){if(c=='-')f=-;c=getchar();}
while(c>='' && c<=''){x=x*+c-'';c=getchar();}
return x*f;
} const int MAXN = ; int mod;
int a[MAXN];
int dp[MAXN][MAXN]; int add(int a, int b)
{
return (a + b) % mod;
} int main()
{
int n, m, b;
cin >> n >> m >> b >> mod;
for (int i = ; i < n; i++)
cin >> a[i];
dp[][] = ;
for (int i = ; i < n; i++)
for (int j = ; j <= m; j++)
for (int k = a[i]; k <= b; k++)
dp[j][k] = add(dp[j][k], dp[j - ][k - a[i]]);
int ans = ;
for (int i = ; i <= b; i++)x
ans = add(ans, dp[m][i]);
cout << ans << endl;
}
Review:
比较容易的一道题
B
题解:
首先可以通过BFS得到dis[i][j]数组,也就是任意两点的最短路
然后分两种情况
1. 只剩下s1到t1,s2到t2的最短路 代价:m-dis[s1][t1]-dis[s2][t2]
2. 剩下的两条路径有交,组成一个‘H’形状
代价:枚举i,j表示那个交集的两端
然后剩下的边数的可能情况就是dis[s1][i]+dis[s2][i]+dis[i][j]+dis[j][t1]+dis[j][t2] 或者 dis[s1][i]+dis[t2][i]+dis[i][j]+dis[j][t1]+dis[j][s2]
取一个最小值 再用m减去就好了
注意判断这样的i,j不满足l1,l2的要求的情况
Code:
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=,f=;char c=getchar();
while(c<'' || c>''){if(c=='-')f=-;c=getchar();}
while(c>='' && c<=''){x=x*+c-'';c=getchar();}
return x*f;
} const int maxn=;
int n,m;
vector<int> gr[maxn];
int dis[maxn][maxn];
bool vis[maxn]; void bfs(int u){
queue<int> q;
while(!q.empty()) q.pop();
memset(vis,,sizeof(vis));
vis[u]=;q.push(u);
while(!q.empty()){
int v=q.front();
q.pop();
for(int i=;i<gr[v].size();i++){
int nw=gr[v][i];
if(vis[nw]) continue;
vis[nw]=;
dis[u][nw]=dis[u][v]+;
q.push(nw);
}
}
} int main(){
n=read(),m=read();
rep(i,,m){
int a=read(),b=read();
gr[a].pb(b),gr[b].pb(a);
}
int s1,t1,l1,s2,t2,l2;
s1=read(),t1=read(),l1=read();
s2=read(),t2=read(),l2=read(); rep(i,,n){
bfs(i);
} if(dis[s1][t1]>l1 || dis[s2][t2]>l2){
puts("-1");
return ;
}
int ans1=m-dis[s1][t1]-dis[s2][t2];
int ans2=;
for(int i=;i<=n;i++){
for(int j=;j<=n;j++){
if(i==j) continue;
int nw=dis[s1][i]+dis[s2][i]+dis[i][j]+dis[j][t1]+dis[j][t2];
if(dis[s1][i]+dis[i][j]+dis[j][t1]>l1 || dis[s2][i]+dis[i][j]+dis[j][t2]>l2) nw=1e9;
int nw2=dis[s1][i]+dis[t2][i]+dis[i][j]+dis[j][t1]+dis[j][s2];
if(dis[s1][i]+dis[i][j]+dis[j][t1]>l1 || dis[t2][i]+dis[i][j]+dis[j][s2]>l2) nw2=1e9;
ans2=max(ans2,max(m-nw,m-nw2));
}
}
printf("%d\n",max(ans1,ans2));
return ;
} /*
9 9
1 2
2 3
2 4
4 5
5 7
5 6
3 8
8 9
9 6
1 7 4
3 6 3
*/
Review:
我竟然连能够求出dis数组都没看出来
傻傻的以为要floyd或者n遍dijkstra
没注意到边权为1。。。
所以边权为1----BFS
C
题解:
很显然要用状压dp
(毕竟20的数据范围摆在那里)
那么令$dp[mask]$表示将mask对应的字符串变成easy to remember的最小代价
复杂度最多只能为$O(2^n \times n)$ 所以还可以枚举一层
那么怎么做呢
考虑转移:
首先我们不能做到O(1)转移
所以不能枚举每一个属于mask的字符串
那么我们必须做到对于任意一个属于mask的字符串 通过我们的转移都能够得到答案
所以必须得有两种转移
我们令选取的字符串为$s_i$,剩下的状态为$nwmask$
1. 把$s_i$改动一位,并压进去
因为最多20个字符串而有26个字母,所以可以保证改任意一位都可行
那肯定选择最少的代价的那一位去修改
2. 改动其他的字符串的某一位,使得他们和$s_i$的这一位都不同
这个代价是可以预处理的
Code:
#include <bits/stdc++.h> #define forn(i, n) for(int i = 0; i < int(n); i++) using namespace std; const int N = ;
const int leng = ;
string s[N];
int cost[N][leng], c[N][leng], sv[N][leng], a[N][leng];
int d[ << N]; int main() {
int n, m;
cin >> n >> m;
forn(i, n) cin >> s[i];
forn(i, n) forn(j, m) cin >> a[i][j]; forn(i, n) {
forn(j, m) {
int curv = ;
forn(k, n) {
if (s[i][j] == s[k][j]) {
sv[i][j] |= ( << k);
c[i][j] += a[k][j];
curv = max(curv, a[k][j]);
}
}
c[i][j] -= curv;
}
}
forn(i, << n) d[i] = ;
d[] = ;
forn(mask, << n) {
if (mask == ) continue;
int lowbit = -;
forn(i, n) {
if ((mask >> i) & ) {
lowbit = i;
break;
}
}
forn(i, m) {
d[mask] = min(d[mask], d[mask & (mask ^ sv[lowbit][i])] + c[lowbit][i]);
d[mask] = min(d[mask], d[mask ^ ( << lowbit)] + a[lowbit][i]);
}
}
printf("%d\n", d[( << n) - ]);
return ;
}
Review:
我觉得挺难的,但是怎么那么多人随手切啊
D
题解:
首先如果只要算1为根的答案是个很简单的树形dp
用$dp[i]$表示以i为根的子树的答案
$dp[i]=\prod {(dp[son]+1)}$
然后我们考虑怎么算每个点的答案
我们通过父亲的答案求儿子的答案
怎么做呢?
首先我们看从父亲到儿子有哪些变化
我们用ans[i]记录i结点对应的上方给i的贡献 然后再乘上dp[i](i的下方对i的贡献)就是i为根的答案
我们怎么计算ans数组呢
发现从父亲到儿子的变化就在于多了几个儿子对应的兄弟
$ans[son]=ans[father] \times \prod {(dp[brother]+1)}$
所以我们记录L,R表示当前结点的儿子们的前缀(dp+1)的积,后缀(dp+1)的积
然后$ans[son]=ans[father]*L[son]*R[son]*ans[father]+1$就可以了
+1指的是把儿子的父亲当作儿子的儿子的时候计算答案的时候要把dp+1相乘
Code:
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=,f=;char c=getchar();
while(c<'' || c>''){if(c=='-')f=-;c=getchar();}
while(c>='' && c<=''){x=x*+c-'';c=getchar();}
return x*f;
} const int mod=;
int n;
vector<int> adj[];
ll dp[],L[],R[];
ll ans[]; void dfs(int x){
dp[x]=;
for(int i=;i<adj[x].size();i++){
int v=adj[x][i];
dfs(v);
dp[x]=dp[x]*(dp[v]+)%mod;
}
} void dfs2(int x){
int num=adj[x].size();
if(num==) return;
L[]=,R[num-]=ans[x];
for(int i=;i<adj[x].size();i++){
int v=adj[x][i];
L[i+]=L[i]*(dp[v]+)%mod;
}
for(int i=num-;i>=;i--){
int v=adj[x][i];
R[i-]=R[i]*(dp[v]+)%mod;
}
for(int i=;i<num;i++){
int v=adj[x][i];
ans[v]=(L[i]*R[i]+)%mod;
}
for(int i=;i<num;i++){
int v=adj[x][i];
dfs2(v);
}
} int main(){
n=read();
rep(i,,n){
int x=read();
adj[x].pb(i);
}
dfs();
ans[]=;
dfs2();
rep(i,,n)
printf("%lld ",dp[i]*ans[i]%mod);
puts("");
return ;
} /*
5
1 2 3 4
*/
Review:
第一个dp很好想
但是怎么求ans不知道怎么想的
可能需要灵机一动?
Codeforces Round #302 (Div. 1) 训练的更多相关文章
- 完全背包 Codeforces Round #302 (Div. 2) C Writing Code
题目传送门 /* 题意:n个程序员,每个人每行写a[i]个bug,现在写m行,最多出现b个bug,问可能的方案有几个 完全背包:dp[i][j][k] 表示i个人,j行,k个bug dp[0][0][ ...
- 构造 Codeforces Round #302 (Div. 2) B Sea and Islands
题目传送门 /* 题意:在n^n的海洋里是否有k块陆地 构造算法:按奇偶性来判断,k小于等于所有点数的一半,交叉输出L/S 输出完k个L后,之后全部输出S:) 5 10 的例子可以是这样的: LSLS ...
- 水题 Codeforces Round #302 (Div. 2) A Set of Strings
题目传送门 /* 题意:一个字符串分割成k段,每段开头字母不相同 水题:记录每个字母出现的次数,每一次分割把首字母的次数降为0,最后一段直接全部输出 */ #include <cstdio> ...
- Codeforces Round #549 (Div. 2) 训练实录 (5/6)
The Doors +0 找出输入的01数列里,0或者1先出完的的下标. Nirvana +3 输入n,求1到n的数字,哪个数逐位相乘的积最大,输出最大积. 思路是按位比较,从低到高,依次把小位换成全 ...
- Codeforces Round #302 (Div. 1) C. Remembering Strings DP
C. Remembering Strings Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/5 ...
- Codeforces Round #302 (Div. 2) D - Destroying Roads 图论,最短路
D - Destroying Roads Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/544 ...
- Codeforces Round #302 (Div. 2) C. Writing Code 简单dp
C. Writing Code Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/544/prob ...
- Codeforces Round #302 (Div. 2) B. Sea and Islands 构造
B. Sea and Islands Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/544/p ...
- Codeforces Round #302 (Div. 2) A. Set of Strings 水题
A. Set of Strings Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/544/pr ...
随机推荐
- 高仿美团iOS版,版本5.7
高仿美团iOS版,版本:5.7 iOS技术交流群:112365317 github链接:https://github.com/lookingstars/meituan 假设你认为不错.欢迎star 哦 ...
- Alert提示框之后跳转指定页面
<li onclick="closes();">BTC</li> alert跳转到指定页面 <script type="text/javas ...
- Javascript对象的技巧和陷阱
创建对象的3种方法 方法1 直接创建 var obj = { name: "mike", age: 10 } 方法2 用new创建 var ob = new Date(); 方法3 ...
- 《从零開始学Swift》学习笔记(Day 61)——Core Foundation框架之内存管理
原创文章,欢迎转载. 转载请注明:关东升的博客 在Swift原生数据类型.Foundation框架数据类型和Core Foundation框架数据类型之间转换过程中,尽管是大部分是能够零开销桥接,零开 ...
- babel的安装和使用方法
要使用Babel, 我们需要nodeJS的环境和npm, 主要安装了nodeJS, npm就默认安装了 , 现在安装nodeJS很简单了, 直接下载安装就好了: 安装es-checker 在使用Bab ...
- Spring Boot 整合 Listener
两种方法: 方法一: 使用注解 编写Listener,并使用@WebListener注解标记,在启动类使用注解:@ServletComponentScan package clc.user.liste ...
- IntelliJ IDEA 注册码 有效期截止于2018/10/14
来源: http://idea.lanyus.com/ IntelliJ IDEA 注册码: EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW ...
- zoj 3204 Connect them(最小生成树)
题意:裸最小生成树,主要是要按照字典序. 思路:模板 prim: #include<iostream> #include<stdio.h> #include<string ...
- 关于JQuery获取元素索引值的理解
举例: <html> <body> <div id="text"> <div> <a><span>标题一&l ...
- Objective-C Runtime(二)消息传递机制
在对象上调用方法是包括Objective-C的众多语言都具备的功能.但在Objective-C中,这个术语叫『传递消息』(pass a message).『消息』有「名称」(name)或「选择子」(s ...