A Codeforces Round #457 (Div. 2) C. Jamie and Interesting Graph
题意:
给你 n, m, 让你构造一个图出来,n 个点, m 条边,每条边有权重。 没有重边,没有自环。
且从 1 号点到 n 号点最短路的长度是素数。这个图组成的最小生成树的权重和也是素数。
思路:
先挑一个素数 x
出来, x
大于等于 n,
然后我们先连个最短路出来,这个最短路也是最小生成树。
我们把 i 和 i+1 连起来,其中 1 和 2 边的权重为 x - (n - 2)
其他边的权重都设置为 1.
然后我们还剩下m - (n - 1)
条边没有构造,这些边就随便搞搞, 权重设置很大,一定要大于 1 和 2 边的权重, 这样就不会影响最小生成树和最短路。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int n,m;
bool vis[N];
vector<int>f;
void solve(){
vis[1] = 1;
for (int i = 2; i < N; ++i){
if (vis[i] == 0) {
f.push_back(i);
for (int j = i * 2; j < N; j+= i)
vis[j] = 1;
}
}
}
int main(){
solve();
scanf("%d%d",&n,&m);
int tmp = lower_bound(f.begin(),f.end(),n) - f.begin();
tmp = f[tmp];
int k = tmp * 100;
printf("%d %d\n",tmp, tmp);
for (int i = 1; i < n; ++i){
if (i == 1) printf("%d %d %d\n",i,i+1,tmp - (n - 2));
else printf("%d %d 1\n",i,i+1);
}
m = m - (n - 1);
for (int i = 1; i <= n; ++i)
for (int j = i + 2; j <= n; ++j){
if (m == 0) return 0;
printf("%d %d %d\n",i,j,k);
m--;
}
return 0;
}
B Codeforces Round #428 (Div. 2) C. Journey
题意:
给你一个n个节点的树, 从一号节点开始,不断朝邻接点走,直到不能走为止,用 长度 * 走到该点的概率,最后加起来。
每个点只能走一次。
思路:
就跟题意一样的了, 一个dfs就行了
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
vector<int>f[N];
int d[N];
double val[N],ans;
int n,m;
void dfs(int u, int fa){
int len = f[u].size() - 1;
d[u]= d[fa] + 1;
for (int v: f[u]){
if (v == fa) continue;
val[v] = val[u] * (1.0/ len);
dfs(v, u);
}
if (len == 0) ans += val[u] * d[u];
}
int main(){
int x,y;
scanf("%d",&n);
for (int i = 1; i <= n; ++i)
val[i] = 1;
for (int i = 1; i < n; ++i){
scanf("%d%d",&x,&y);
f[x].push_back(y);
f[y].push_back(x);
}
f[1].push_back(0);
d[0] = -1;
dfs(1,0);
printf("%.6lf\n",ans);
return 0;
}
C Educational Codeforces Round 57 (Rated for Div. 2) D. Easy Problem
题意:
现在有一个字符串,你需要用ai的钱去掉这个字符串的第i个位置的字符。现在要使得该字符串中不包含子序列hard。求最小钱数。
思路:
用 四个变量 h a r d 分别表示, 到 i 这个位置,
不能组成 h 的价值
不能组成 ha 的价值
不能组成 har 的价值
不能组成 hard 的价值。
- 当前位置的字符是 h 的时候
要想不组成 h , 那就一定要把 h 删除掉, - 当前位置的字符是 a 的时候
要想不组成 ha, 要么把前面的 h 删除掉, 要么把当前的字符 a 删除掉 - 当前位置的字符是 r 的时候,
要想不组成 har, 要么把前面的 a 删除掉, 那么把当前的字符 r 删除掉,
a 代表前面组不成 ha , 所以当前字符 r 留着没有事,
如果不选择删除a, 那就代表前面有可能组成 ha ,但是我把 r 删除掉,同样不能组成 har, - 当前字符事 d 的时候,
要想不组成 hard, 那么把前面的 r 删除掉, 要么把当前的 d 删除掉。
r 代表前面的不能组成 har, 所以当前的字符 d留着没有事,
如果不删除 r, 代表前面的有可能组成 har, 所以字符d就必须删除掉。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
char s[N];
int n,m;
long long h, r, a, d;
int main(){
scanf("%d",&n);
scanf("%s",s+1);
for (int i = 1; i <= n; ++i){
scanf("%d",&m);
if (s[i] == 'h'){
h += m;
} else if (s[i] == 'a'){
a = min(h, a + m);
} else if (s[i] == 'r'){
r = min(a, r + m);
} else if (s[i] == 'd')
d = min(r, d + m);
}
printf("%lld\n",d);
return 0;
}
D Codecraft-18 and Codeforces Round #458 (Div. 1 + Div. 2, combined) C. Travelling Salesman and Special Numbers
题意:
x 操作一次变成 x 的二进制中 1 的个数。
然后问 [1 , n] 中, 有多少个数,操作 k 次正好变成 1.
这个 n 很大, 2的1000次方。
思路:
随便一个数,操作一次之后就会变成小于等于1000 的数, 所以就可以预先处理出来1000以内的数经过几次操作就变成了1.
所以我们做的时候,就先操作一次,然后让他变成一个小于等于1000的数,然后这个数操作 k-1 次变成 1就好了。 记在数组 cnt 中。
然后就可以用排列组合。
从高位到低位进行枚举, 当前位前面已经有了 num 个1.。
当前位是0,那就必须填0, 跳过,
当前位是1, 那就当前位先填0,枚举1000 以内的数 j,如果 cnt[j] == k - 1 那么我们就需要1的个数组成 j 个, 既然前面有num 个1, 那么后面的位置上就有 j - num 个1, 后面的位置上用排列组合计算出来。
有几个特殊情况:
- 当 k == 0 的时候, 直接输出 1。
- 当 k == 1 的时候,1 这个数字会被多计算一次, 所以答案要减一。
- 当枚举到最后一个1的时候, 我们会算当前位是0的情况, 并不会算当前位是1的情况, 所以这个情况也要特判一下。
#include<bits/stdc++.h>
#define popcnt __builtin_popcount
using namespace std;
const int N = 1008;
const int mod = 1e9 + 7;
typedef long long ll;
char s[N];
int k,cnt[N],num;
ll fac[N],inv[N],ans;
ll mul(ll a, ll n, ll p){
ll ans = 1;
a %= p;
while(n){
if (n & 1) ans = (ans * a) % p;
a = (a * a) % p;
n >>= 1;
}
return ans;
}
void init(){
scanf("%s",s);
scanf("%d",&k);
for(int i = 2; i < N; ++i)
cnt[i] = cnt[popcnt(i)] + 1;
fac[0] = 1;
for (int i = 1; i < N; ++i)
fac[i] = (fac[i-1] * i) % mod;
inv[N-1] = mul(fac[N-1], mod - 2, mod);
for (int i = N - 2; i >= 0; --i)
inv[i] = (inv[i+1] * (i + 1)) % mod;
}
ll C(int n, int m){
if (n < m) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
init();
if (k == 0) return puts("1")*0;
int len = strlen(s);
for (int i = 0; i < len; ++i){
if (s[i] == '0') continue;
for (int j = max(num, 1); j < len; ++j)
if (cnt[j] == k - 1) ans = (ans + C(len-i-1,j-num))%mod;
num++;
}
if (k == 1) ans = (ans - 1 + mod) % mod;
if (cnt[num] == k-1) ans = (ans + 1)%mod;
printf("%lld\n",ans);
return 0;
}
E Codeforces Round #562 (Div. 2) E. And Reachability
题意:
有一个数组 ,n 个数,
询问 x y,
是否可以在 [x, y] 这个区间内按照顺序找几个数出来, 找出来的数中,相邻两个数 & 起来要大于0,
x, y 位置的数必须选择。
思路:
我们有 f[i][j] 代表 从i 这个位置开始, 最小能到达的位置 y ,这个 y 位置上的值 的二进制的 j 位是1,且从 i 位置到 y 位置,满足题目要求。
简单来说就是 区间 [ i, f[i][j] ]
, 这个区间满足题目的要求,且 f[i][j] 这个位置上的数的二进制的j位是1.
所以我们首要的问题就是预处理出来f[i][j]
, 从后向前预处理。
明确一个数组的含义 w[j] 代表距离当前位最近的位置上的数的二进制的 j 位为1,
for (int i = n; i > 0; --i)
for (int j = 0; j < 25; ++j)
if ((a[i] >> j) & 1){ //枚举当前数二进制的各个位置上的数是不是1.
for (int k = 0; k < 25; ++k)
if (w[j] != -1) f[i][k] = min(f[i][k],f[w[j]][k]); //判断是不是有二进制 j 位为1的数。如果有就更新。
w[j] = i; f[i][j] = min(f[i][j],i);
}
最后询问的时候。
我们枚举二进制的各个数位 i,
如果 f[x][i] <= y && (a[y] >> i) & 1
说明就满足题目的条件。
也就说明有一个中间位置 T, 这个位置上的数二进制的 i 位 是1, 且 y 位置上的数的二进制的 i 位 也是 1. & 起来肯定大于0.
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+100;
const int INF = 0x3f3f3f3f;
int a[N],w[25],f[N][25];
int n,m;
void init(){
memset(f, INF , sizeof f);
memset(w , -1 , sizeof w);
for (int i = 1; i <= n; ++i)
scanf("%d",&a[i]);
for (int i = n; i > 0; --i)
for (int j = 0; j < 25; ++j)
if ((a[i] >> j) & 1){
for (int k = 0; k < 25; ++k)
if (w[j] != -1) f[i][k] = min(f[i][k],f[w[j]][k]);
w[j] = i; f[i][j] = min(f[i][j],i);
}
}
int main(){
scanf("%d%d",&n,&m);
init();
int x,y;
bool vis;
for (int i = 0; i < m; ++i){
scanf("%d%d",&x,&y);
vis = 0;
for (int j = 0; j < 25; ++j)
if ((a[y] >> j)&1 && (f[x][j] <= y)){
vis = 1; break;
}
vis == 1? puts("Shi"): puts("Fou");
}
return 0;
}
F Codeforces Round #577 (Div. 2) B. Zero Array
题意:
给你一个数组, n 个数, 每次选择 i j, i != j, 两个位置上的数同时减一。
问最后是不是可以减到全部为0.
思路:
加起来的数是奇数, 不行
最大的数大于 sum / 2 不行, sum 是加起来的总和。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
int n,m;
void dbg() {cout << endl;}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
long long sum,mx;
int main(){
scanf("%d",&n);
for (int i = 0; i < n; ++i){
scanf("%d",&m);
mx = max(mx,1ll*m);
sum += m;
}
if (sum & 1){
puts("NO");
return 0;
}
if (mx > sum / 2){
puts("NO");
} else puts("YES");
return 0;
}
G VK Cup 2016 - Round 2 E. Little Artem and Time Machine
题意:
n 个操作,
x y z,
- x == 1 在 y 时间插入一个数 z,
- x == 2 在 y 时间删除一个数 z。
- x == 3 在 y 时间询问数 z 出现的次数
思路:
当考虑只有一个数的时候, 那就是简单的树状数组了,
- 若 x == 1, 那就再 y 的位置上 + 1,
- 若 x == 2, 那就再 y 的位置上 - 1,
最后询问就可以了,
但是这个题插入的数并不一样怎么办呢,
我们把所有的数据读入,然后按照数的大小排序, 把相同的数聚集再一起, 然后用树状数组, 当一个数用完的时候,把树状数组清空就好了, 由于 y 很大, 所以需要离散化。
还有一种方法就是暴力树状数组了, 用map 当数组。写法更简单。 网上有好多这样的代码。
#include<bits/stdc++.h>
#define low(x) (x & (-x))
using namespace std;
const int N = 1e5+100;
void dbg() {cout << endl;}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
struct node{
int a,b,c,id;
bool operator < (const node &A) const{
if (c != A.c) return c < A.c;
return id < A.id;
}
}e[N];
int n,m,ans[N],c[N];
vector<int>f;
void init(){
scanf("%d",&n);
memset(ans, -1, sizeof ans);
for (int i = 0; i < n; ++i){
scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
f.push_back(e[i].b);
e[i].id = i;
}
sort(f.begin(), f.end());
for (int i = 0; i < n; ++i)
e[i].b = lower_bound(f.begin(), f.end(), e[i].b) - f.begin() + 1;
sort(e,e+n);
// for (int i = 0; i < n; ++i)
// printf("%d %d %d\n",e[i].a,e[i].b,e[i].c);
}
void add(int x, int y){
for (; x <= n+10; x += low(x))
c[x] += y;
}
int ask(int x){
int ans = 0;
for(; x; x -= low(x))
ans += c[x];
return ans;
}
int main(){
init();
int l = 0,r;
while(l < n){
r = l;
while(r < n && e[l].c == e[r].c){
if (e[r].a == 1) add(e[r].b, 1);
if (e[r].a == 2) add(e[r].b, -1);
if (e[r].a == 3) ans[e[r].id] = ask(e[r].b);
r++;
}
for (int i = l; i < r; ++i){
if (e[i].a == 1) add(e[i].b, -1);
if (e[i].a == 2) add(e[i].b, 1);
}
l = r;
}
for (int i = 0; i < n; ++i)
if (ans[i] != -1) printf("%d\n",ans[i]);
return 0;
}
H
#include<bits/stdc++.h>
using namespace std;
long long n,m,mx,mn;
int main(){
scanf("%lld%lld",&n,&m);
if (m == n || m == 0) mn = 0; else mn = 1;
if (n >= m * 3) {
mx = m * 2;
} else mx = n - m;
printf("%lld %lld\n",mn,mx);
return 0;
}
I
模拟就好了。
注意double 的数不能过大, 不然会丢失精度。 所以每次控制一下double 的大小。
#include<bits/stdc++.h>
using namespace std;
double n,m;
int k;
double solve(double tmp){
double sum = tmp;
long long c = (long long) (tmp / n);
tmp = tmp - c * n;
int y = c % 4;
if (y == 0){
printf("%.10lf %.10lf\n",tmp, 0.0);
} else if (y == 1){
printf("%.10lf %.10lf\n",n, tmp);
} else if (y == 2){
printf("%.10lf %.10lf\n",n - tmp,n);
} else{
printf("%.10lf %.10lf\n",0.0,n - tmp);
}
return sum - 1ll*floor(sum / (n * 4))*4*n;
}
int main(){
scanf("%lf%lf",&n,&m);
scanf("%d",&k);
double tmp = 0;
for (int i = 1; i <= k; ++i){
tmp += m;
tmp = solve(tmp);
}
return 0;
}