题意:给定一个骰子,有若干个面,当掷到某个面得到一定的钱。某些面可以再掷一次,问掷一次获得钱的期望
投一次色子的期望是: s u m n \frac{sum}{n} nsum
2次的期望: s u m n + m n ∗ s u m n \frac{sum}{n} + \frac{m}{n}*\frac{sum}{n} nsum+nm∗nsum
n次的概率是: s u m n + m n ∗ s u m n + . . . + ( m n ) n − 1 ∗ s u m n \frac{sum}{n} + \frac{m}{n}*\frac{sum}{n} + ... + (\frac{m}{n})^{n-1}*\frac{sum}{n} nsum+nm∗nsum+...+(nm)n−1∗nsum
等比数列前n项和,设 s u m n = p , m n = q ( 0 < = q < = 1 ) \frac{sum}{n}=p, \frac{m}{n}=q (0<=q<=1) nsum=p,nm=q(0<=q<=1), ( 1 − q n ) ∗ p 1 − q \frac{(1-q^n)*p}{1-q} 1−q(1−qn)∗p
p == 0, ans = 0; q == 1, ans = inf, q < 1, q n q^n qn ->无穷小, ans = p 1 − q \frac{p}{1-q} 1−qp
#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define pb push_back
#define mk make_pair
const int N = 200+10;
const int mod = 1e9+7;
inline ll read()
{
ll x=0,w=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
return w==1?x:-x;
}
ll qpow(ll a, int x){
ll res = 1;
while(x){
if(x&1) res = res * a % mod;
a = a * a % mod; x >>= 1;
}
return res;
}
int n, a[N], m, b[N];
int main(){
while(~scanf("%d", &n)){
double sum = 0;
for(int i = 1; i <= n; i++){
a[i] = read();
sum += a[i];
}
double p = sum / n;
m = read();
int cnt = 0;
for(int i = 1; i <= m; i++){
b[i] = read();
}
double q = m*1.0 / n;
if(p < 1e-10){
printf("0.00\n");
}
else if(q < 1 + 1e-10 && q > 1 - 1e-10){
printf("inf\n");
}
else{
printf("%.2llf\n", p / (1 - q));
}
}
return 0;
}
题意:给一个无向图,求删掉两个点之后最多联通块数量。
枚举删除一个点,然后tarjan求割点,答案为联通块数 + cut[j],取max
#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e4+10;
const int mod = 1e9+7;
inline ll read()
{
ll x=0,w=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
return w==1?x:-x;
}
int n, m;
struct Edge{
int to, next;
}e[N];
int head[N], tot = 0;
void addEdge(int u, int v){
e[tot] = {v, head[u]};
head[u] = tot++;
}
int low[N], dfn[N], cut[N], index;
void init(){
memset(low, 0, sizeof(int)*(n+1));
memset(dfn, 0, sizeof(int)*(n+1));
memset(cut, 0, sizeof(int)*(n+1));
index = 0;
}
void tarjan(int u, int fa, int del){
low[u] = dfn[u] = ++index;
int pre_cnt = 0, son = 0;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(v == del) continue;
if(v == fa && pre_cnt == 0) {
pre_cnt++; continue;
}
if(!dfn[v]){
son++;
tarjan(v, u, del);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]){
cut[u] ++;
}
}
else if(u != fa && low[u] > dfn[v]){
low[u] = dfn[v];
}
}
if(u == fa) { //根节点
cut[u] = son - 1;
}
}
int main(){
while(~scanf("%d %d", &n, &m)){
memset(head, -1, sizeof(int)*(n+1));
tot = 0;
int u, v;
for(int i = 0; i < m; i++){
scanf("%d %d", &u, &v);
addEdge(u, v);
addEdge(v, u);
}
int ans = 0;
for(int i = 0; i < n; i++){
init();
int cnt = 0;
for(int j = 0; j < n; j++){
if(i == j) continue;
if(!dfn[j]){
tarjan(j, j, i);
cnt++;
}
}
for(int j = 0; j < n; j++){
if(i == j) continue;
ans = max(ans, cnt + cut[j]);
}
}
printf("%d\n", ans);
}
return 0;
}
题意:把 1 − n 1-n 1−n这 n n n个数,分成 m m m堆,使得 m m m堆数的和相等。
无解的情况肯定是 s u m m < n ∣ ∣ s u m % m ! = 0 \frac{sum}{m} < n || sum\%m != 0 msum<n∣∣sum%m!=0
将较大的数,每 2 × m 2\times m 2×m为一组,放入 m m m堆中,从小到大相应配对,显然和相等
当剩下的数小于4m-1时,暴力匹配,
当n!= m时,要想分成m堆, n最小为2m-1,1 和 n-1一组, 2和n-2一组。。。n单独为一组,所以当每2m为一组时,只有n-2m >= 2m-1,才能分出一组, n < 4m-1时暴力匹配。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;
int t, n, m;
vector<int> g[N];
void printYes(){
printf("YES\n");
for(int i = 0; i < m; i++){
printf("%d ", g[i].size());
for(int j = 0; j < g[i].size(); j++){
printf("%d%c", g[i][j], " \n"[j == g[i].size()-1]);
}
}
}
int vis[100], ave;
int dfs(int cur, int sum, int num){
if(cur == m){
return 1;
}
for(int i = n; i >= num; i--){
if(vis[i]) continue;
if(sum + i == ave){
vis[i] = 1;
if(dfs(cur+1, 0, 1)){
g[cur].push_back(i);
return 1;
}
vis[i] = 0;
return 0;
}
else{
vis[i] = 1;
if(dfs(cur, sum+i, i)){
g[cur].push_back(i);
return 1;
}
vis[i] = 0;
}
}
return 0;
}
int main(){
scanf("%d", &t);
while(t--){
scanf("%d %d", &n, &m);
ll sum = 1ll * (1 + n) * n / 2;
if(sum%m || sum / m < n){
printf("NO\n");
}
else{
for(int i = 0; i <= m; i++){
g[i].clear();
}
while(n -2*m >= 2*m - 1){
for(int i = 0; i < m; i++){
g[i].push_back(n-i);
g[i].push_back(n-2*m+i+1);
}
n -= 2*m;
}
sum = 1ll * (1 + n) * n / 2;
ave = sum / m;
memset(vis, 0, sizeof(vis));
dfs(0, 0, 1);
printYes();
}
}
return 0;
}
题意:给一棵树,每个节点有个权值。若干次查询,每次查询一棵子树中的数与一个数异或最大为多少。
来自于:xls
把每个点权拆成二进制构造字典树,假设每次查找的是 1,x,那么直接从字典树中找最大值(默认这个大家都会),如果是 u,x,那么我只需要只保存 u 子树的字典树,这个时候我们借助dfs序和可持久化操作建字典树,第 i 颗字典树保存字典序 1 到 i 个点的信息,id [ u ]保存 u 节点的dfs序编号,sz[ u ]保存 u 子树的大小,每次查询 u,x,通过 r t [ i d [ u ] + s z [ u ] − 1 ] − r t [ i d [ u ] − 1 ] rt[ id[u] + sz[u] - 1] - rt[ id[u] - 1] rt[id[u]+sz[u]−1]−rt[id[u]−1]就可以得到我们想要的字典树,难点解决
#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;
inline ll read()
{
ll x=0,w=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
return w==1?x:-x;
}
int n, q, a[N];
struct Edge{
int to, next;
}e[N];
int head[N], tot = 0;
void addEdge(int u, int v){
e[tot] = {v, head[u]};
head[u] = tot++;
}
int rt[N], ls[N*31], rs[N*31], sum[N*31],cnt = 0;
void up(int &o, int pre, int k, int v){
o = ++cnt;
ls[o] = ls[pre];
rs[o] = rs[pre];
sum[o] = sum[pre] + 1;
if(k < 0)
return ;
if(v & (1<<k))
up(rs[o], rs[pre], k-1, v);
else
up(ls[o], ls[pre], k-1, v);
}
int id[N], sz[N], index = 0;
void dfs(int u){
id[u] = ++index;
sz[u] = 1;
up(rt[index], rt[index-1], 29, a[u]);
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
dfs(v);
sz[u] += sz[v];
}
}
int qu(int o, int pre, int k, int v){
if(k < 0)
return 0;
if(v & (1<<k)){
if(sum[ls[o]] > sum[ls[pre]])
return (1<<k) + qu(ls[o], ls[pre], k-1, v);
else
return qu(rs[o], rs[pre], k-1, v);
}
else{
if(sum[rs[o]] > sum[rs[pre]])
return (1<<k) + qu(rs[o], rs[pre], k-1, v);
else
return qu(ls[o], ls[pre], k-1, v);
}
}
int main(){
while(~scanf("%d %d", &n, &q)){
memset(head, -1, sizeof(int)*(n+1));
tot = 0; index = 0; cnt = 0;
for(int i = 1; i <= n; i++){
a[i] = read();
}
int fa;
for(int i = 2; i <= n; i++){
fa = read();
addEdge(fa, i);
}
dfs(1);
int u, x;
while(q--){
u = read(); x = read();
printf("%d\n", qu(rt[id[u]+sz[u]-1], rt[id[u]-1], 29, x));
}
for(int i = 0; i <= cnt; i++){
sum[i] = ls[i] = rs[i] = 0;
}
}
return 0;
}
题意:给出一条链,求链上边的子集,使得起点到终点了联通。子链中边出现的顺序必须和原链一样,子链最短。
建图,跑最短路,原链中给每个点编号 1-n, 并且按照新编号连边,然后如果前面出现了这个点,那么就从上一个点连一条边权为0的边到现在这个点,这样建图跑最短路,边出现的顺序就会和原链一样。
#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;
inline ll read()
{
ll x=0,w=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
return w==1?x:-x;
}
ll qpow(ll a, int x){
ll res = 1;
while(x){
if(x&1) res = res * a % mod;
a = a * a % mod; x >>= 1;
}
return res;
}
int n;
int a[N];
struct Edge{
int to, next, w;
}e[2*N];
int head[N], tot = 0;
void addEdge(int u, int v, int w){
e[tot] = {v, head[u], w};
head[u] = tot++;
}
int d[N], vis[N], pre[N];
priority_queue< pair<int, int> > q;
void Dij(){
for(int i = 1; i <= n; i++){
d[i] = 1e9;
vis[i] = pre[i] = 0;
}
d[1] = 0;
q.push(mk(0, 1));
while(!q.empty()){
int u = q.top().second;
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(d[v] > d[u] + e[i].w){
d[v] = d[u] + e[i].w;
pre[v] = u;
q.push(mk(-d[v], v));
}
}
}
}
int main(){
n = read();
memset(head, -1, sizeof(int)*(n+1));
tot = 0;
int u, v;
for(int i = 1; i <= n; i++){
a[i] = read();
}
vis[a[1]] = 1;
for(int i = 1; i < n; i++){
addEdge(i, i+1, 1);
if(vis[a[i+1]]) {
addEdge(vis[a[i+1]], i+1, 0);
//cout << vis[a[i+1]] << " " << i+1 << "..\n";
}
vis[a[i+1]] = i+1;
}
Dij();
vector<int> ans;
int t = n;
while(t){
if(!ans.size() || ans[ans.size()-1] != a[t])
ans.push_back(a[t]);
t = pre[t];
}
for(int i = ans.size()-1; i >= 0; i--){
printf("%d%c", ans[i], " \n"[i==0]);
}
//cout << d[n] << "\n";
return 0;
}
题意:给出一个数的数字和以及数字的平方和,求最小满足题意的数。
因为最多只有100位,所以s1 <= 900, s2 <= 8100,
dp[i][j][0] 表示 和为i 平方和为j 的数最小长度,dp[i][j][1] 存的是当前选的数
从小往大选,严格小于才更新,这样可以保证长度最小的同时,值最小。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define mk make_pair
const int N = 1e5+10;
const int mod = 1e9+7;
int t, s1, s2;
int dp[901][8101][2];
void init(){
for(int i = 0; i <= 900; i++){
for(int j = 0; j <= 8100; j++){
dp[i][j][0] = 200;
}
}
dp[0][0][0] = 0;
for(int i = 1; i <= 900; i++){
for(int j = 1; j <= 8100; j++){
for(int k = 1; k <= 9; k++){
if(i - k >= 0 && j - k*k >= 0){
if(dp[i][j][0] > dp[i-k][j-k*k][0] + 1){
dp[i][j][0] = dp[i-k][j-k*k][0] + 1;
dp[i][j][1] = k;
}
}
}
}
}
}
int main(){
init();
scanf("%d", &t);
while(t--){
scanf("%d %d", &s1, &s2);
if(s1 > 900 || s2 > 8100 || dp[s1][s2][0] > 100){
printf("No solution\n");
}
else {
vector<int> v;
while(dp[s1][s2][0]){
int p = dp[s1][s2][1];
v.push_back(dp[s1][s2][1]);
s1 -= p; s2 -= p*p;
}
sort(v.begin(), v.end());
for(int i = 0; i < v.size(); i++){
printf("%d", v[i]);
}
printf("\n");
}
}
return 0;
}