2019暑期第一届何山杯题解

Day1

在这里插入图片描述
分析:
贪心即可,从最高位开始扫,找1

/*
对于2进制,越高位有1,则数字越大。 
算法思想:
	此题本质就是统计序列中每一个2进制位上是1的数的个数是否大于2个,若大于2个则意味着
	最终该位&的结果,其该位上一定是1.那么按照从高位到低位来依次处理就可以解决。 
算法步骤: 
从最高位到最低位(i = 30 ~ 0)依次枚举判断:
1、判断当前第 i 位上是 1 的数并统计记录下来,为节省空间,可以复用原数组(覆盖)来记录产生的新序列,
   即把第 i 位不是 1 的数全部舍弃,只保留第 i 位是 1 的数;  
2、从上面记录产生的新序列中(即第i位都是1的数),继续按照上述办法来处理第i+1位,直到判断完最后一位(i=0); 
3、最后统计 
 

*/ 
#include<cstdio>
using namespace std;

int n,a[300002],d[35] = {0},ans = 0; //2^30>1000000000

void init(){
	scanf("%d",&n);
	for(int i = 1; i <= n; i++) 
		scanf("%d", &a[i]);
}

void find(){
	int i, j, ct, now = n, t;
	for(i = 30; i >= 0; i--){
		ct = 0;
		for(j = 1; j <= now; j++)
			if(a[j] & (1 << i)) ct++;  //统计第i位是1的数有多少个 
	    if(ct < 2) continue;    //当前第 i 位是1的数不够2个,则直接枚举第 i+1 位 
	    t = 0; d[i] = 1;        //d[i]的作用标记有两个以上的数第i位是1 
	    for(j = 1; j <= now; j++)
	    	if(a[j] & (1 << i)) a[++t] = a[j];  //把第 i 位是1的数全部保存在a[1]~a[t]中(节省空间,直接覆盖原数组) 
	    now = t;  //继续在a[1]~a[t]中判断第 i+1 位的情况 
	}
	for(i = 30; i >= 0; i--)
		if(d[i])   ans = ans ^ (1 << i);  //ans初始化为0,用异或运算,可保证第 i 位是 1。 
	
	printf("%d\n",ans);
}
int main()
{
	freopen("and.in","r",stdin);
	freopen("and.out","w",stdout);
	init(); find();
	return 0;
}

在这里插入图片描述
简单的数学期望的题

/*
分析:一个物品会被染色的概率为 1/2,用某种颜色染色的概率为 1/c。

1、40分的方程是用f[i][j][k]表示第i个物品在j次操作次数后颜色变为 k的概率,时间复杂度大概是O(T*N*K*c^2) 

2、60分要考虑到所有物品具有相似性,即 n个物品本质是相同的,所以不用枚举物品。
   f[i][j]表示一个物品操作 i次颜色变为 j的概率。
   满足: f[i+1][j] += f[i][j]*(1/2) 
          f[i+1][(j*b)%c]+=f[i][j]*[(1/2)*(1/c)]
         初始值f[0][1]=1,答案就是Σf[i][j]*j (i表示该箱子的操作次数,0 <= j<c)。复杂度O(T*K*c^2) 
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 55
using namespace std;

int T, n, c, K, cs[MAXN], maxc;
double f[MAXN][MAXN << 1], ans; 

void init(){
	scanf("%d%d%d",&n, &c, &K);
	int x, y;
	memset(cs,0,sizeof(cs));
	maxc = 0;
	for(int i = 1; i <= K; i++){
		scanf("%d%d",&x, &y);
	    for(int j = x; j <= y; j++){
			cs[j]++;     //统计 j 号箱子选择了几次 
			maxc = max(maxc,cs[j]);  //记录所有箱子里选择次数最多的(即最多的操作次数) 
		}
	}
}

void dp(){
	memset(f, 0, sizeof(f));
	f[0][1] = 1;
	for(int i = 0; i < maxc; i++)
		for(int j = 0; j < c; j++){
			f[i+1][j] += f[i][j]/2;
		    for(int k = 0; k < c; k++)
		       f[i + 1][(j * k) % c] += f[i][j] / (2 * c);
		}
	ans = 0;
	for(int i = 1; i <= n; i++)
		for(int j = 0; j < c; j++)
	   		ans += f[cs[i]][j] * j;  //所有箱子颜色编号和的期望值 
	printf("%.9lf\n", ans);
}

int main()
{
	freopen("elephant.in","r",stdin);
	freopen("elephant.out","w",stdout);
	scanf("%d", &T);
	while(T--){
		init(); 
		dp();
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

在这里插入图片描述

最短路的板子,多了一个k,SPFA跑动规。

/*
此题本质:分层图的最短路。

K=1时可以考虑将一点拆成两点。

两层分别按原图建图,然后在第一层向第二层中对应的点连一条长度为 0的单向边;
例如原图有一条 1到 2的双向边。将 1拆成 1和 1+n,2同理;
然后分别在 1、2和 1+n、2+n之间建原长度的双向边;
然后分别在 1、2+n和 2、1+n之间建一条长度为 0的单向边,这样的图能保证至多跑一次长度为 0的边。
在点数边数较少的情况下,K大到 3,4应该也能卡过。
至于正解,可以用一个二维的 dis数组跑最短路,dis[i][j]——表示到达 i点,用了j次急救包的最少流血量。
用最短路做一个类似于dp的东西。答案为dis[T][K].
注意:裸跑spfa会 T三个点,若写spfa记得加 SLF优化。

SLF优化——spfa的SLF优化就是 small label first 优化;
当加入一个新点v的时候如果此时的dis[v]比队首dis[q.front()]还要小的话,就把v点加入到队首,
否则把他加入到队尾,因为先扩展最小的点可以尽量使程序尽早的结束,一种方法可以用模拟队列,
head,tail,但是由于不知道q的数组开多大,可用双端队列 deque<int> q 。
spfa的SLF优化模板:
void spfa(int s)
{
    deque<int>q;
    memset(dis,inf,sizeof(dis));
    memset(use,0,sizeof(use));
    dis[s]=0;
    q.push_back(s);
    use[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop_front();
        use[u]=0;
        for(int i=0;i<(int)edge[u].size();i++)
        {
            int v=edge[u][i].v;
            if(dis[v]>dis[u]+edge[u][i].w)
            {
                dis[v]=dis[u]+edge[u][i].w;
                if(!use[v])
                {
                    use[v]=1;
                    if(!q.empty()&&dis[v]<dis[q.front()])
                        q.push_front(v);
                    else
                        q.push_back(v);
                }
            }
        }
    }
}  
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define MAXN 10002
using namespace std;

int n, m, K, S, T, zz, head[MAXN];

struct bian{
	int to, nx, v;
} e[MAXN * 10];

struct dui{
	int w,c;
} q[MAXN * 10];

int dis[MAXN][12], pd[MAXN][12], ans; 

void insert(int x,int y,int z){ //邻接表
	zz++; e[zz].to = y; e[zz].v = z; e[zz].nx = head[x]; head[x] = zz;
	zz++; e[zz].to = x; e[zz].v = z; e[zz].nx = head[y]; head[y] = zz;
}

void init()
{
	scanf("%d%d%d", &n, &m, &K);
	scanf("%d%d",&S, &T);
	int x,y,z;
	for(int i = 1; i <= m; i++){
		scanf("%d%d%d", &x, &y, &z);
	    insert(x, y, z);
	}
}

void spfa()
{
	int t = 0, w = 1, x, ct, p;
	memset(dis, 127, sizeof(dis));
	q[0].w = S; q[0].c = 0; dis[S][0] = 0; pd[S][0] = 1;
	while(t != w) {
		x = q[t].w; ct = q[t].c; t = (t + 1) % 100000;  
	    for(int i = head[x]; i; i = e[i].nx){
			p = e[i].to;
			if(dis[p][ct] > dis[x][ct] + e[i].v){
				dis[p][ct] = dis[x][ct] + e[i].v;
			    if(!pd[p][ct]){
					if(dis[p][ct] < dis[q[t].w][q[t].c]){
						t = (t + 100000 - 1) % 100000;
						q[t].w = p; q[t].c = ct; pd[p][ct] = 1;
					}
					else{
						q[w].w = p; q[w].c = ct; pd[p][ct] = 1; w = (w + 1) % 100000;
					}
				}
			}
			if(ct + 1 <= K && dis[p][ct + 1] > dis[x][ct]){
				dis[p][ct + 1] = dis[x][ct];
			    if(!pd[p][ct + 1]){
					if(dis[p][ct + 1] < dis[q[t].w][q[t].c]){
						t = (t + 100000 - 1) % 100000;
						q[t].w = p; q[t].c = ct + 1; pd[p][ct+1] = 1;
					}
					else{
						q[w].w = p; q[w].c = ct + 1; pd[p][ct+1] = 1; w = (w + 1) % 100000;
					}
				}
			}
		}
		pd[x][ct] = 0;
	}
	ans = 1 << 30;
	for(int i = 0; i <= K; i++)
	   ans = min(ans, dis[T][i]);
	printf("%d\n",ans);
}

int main()
{
	freopen("move.in","r",stdin);
	freopen("move.out","w",stdout);
	init(); spfa();
	return 0;
}

Day2

在这里插入图片描述
倒着跑,没想到吧。

/*
考虑最后一行,因为其代表文件结束,所以解密后的a=b=c=0。那么我们可以知道倒数第二行的答案(LastAns=-a=-b=-c)。
那么原始式子即转换成一个简单的三元一次式子(只和a,b,c有关),然后这解密后的值又可以由上一行的答案和输入的a0,b0,c0得到,
于是就变成了一个只和LastAns有关系的一元一次式子,所以又可以得到了上一行的答案。所以这样一直算回去就好了。
*/
#include <cstdio>
using namespace std;

int n, cnt, top, ans;
int v[50005], res[500005];
int a[500005], b[500005], c[500005];

int read(){
    int x = 0, f = 1;
	char ch = getchar();
    while(ch < '0' || ch > '9') {
		if(ch == '-') f=-1;
		ch = getchar();
	}
    while(ch >= '0' && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
    return x * f;
}

int main()
{
	freopen("seq.in", "r", stdin);
	freopen("seq.out", "w", stdout);
	n = read();
	for(int i =1 ; i <= n; i++) 
		v[i] = read();
	cnt = 1;
	while(scanf("%lld%lld%lld", &a[cnt], &b[cnt], &c[cnt]) != EOF) 
		cnt++;
	cnt--;
	top = 0;
	res[++top] = ans = -a[cnt--];
	for(int k = cnt; k; k--)
	{
		int xi = v[ans], i = ans;
		int t1 = a[k] * (i + 1) * xi * xi + (b[k] + 1) * i * xi + (c[k] + i);
		int t2 = (i + 1) * xi * xi + i * xi + 1;
		ans = -t1 / t2;
		res[++top] = ans;
	}
    for(int i = top - 1; i; i--) printf("%lld\n", res[i]);
    fclose(stdin);
    fclose(stdout);
	return 0;
}

在这里插入图片描述
组合数学,好好推一下就OK了。

/*
首先头尾连续的无油漆桶段和两个相同颜色油漆桶间的段落不影响答案,去掉不考虑。
然后对于任意两个连续的油漆桶中的段落(假设坐标分别为a,b)可以有b-a种油漆方案,
则所有段落的方案数乘积即为所求。
*/
#include <cstdio>
#include <algorithm>
#define mod 1000000009
#define ll long long
using namespace std;

ll ans = 1;
int n, m;
struct data{
	int val, pos;
} a[100005];

int read()
{
    int x = 0, f = 1;
	char ch = getchar();
    while(ch < '0' || ch > '9')
	{
		if(ch == '-') f = -1;
		ch = getchar();
	}
    while(ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - '0';
		ch = getchar();
	}
    return x * f;
}

bool operator <(data a, data b)
{
	return a.pos < b.pos;
}

int main()
{
	freopen("paint.in", "r", stdin);
	freopen("paint.out", "w", stdout);
	n = read(); m = read();
	for(int i = 1; i <= m; i++)
	{
		char ch[2];
		scanf("%s", ch);
		a[i].val = ch[0]-'A';
		a[i].pos = read();
	}
	sort(a + 1, a + m + 1);
	for(int i = 2; i <= m; i++)
		if(a[i].val != a[i-1].val)
			ans = ans * (a[i].pos - a[i - 1].pos) % mod;
	printf("%lld",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

在这里插入图片描述
差分约束的板子(不会差分约束的我博客里看)

/*
很明显的差分约束系统的题目。 
如果A和B距离至多为 D则建边 A->B权值为 D,距离至少为 D则建边 B->A权值为 -D。
然后最短路。若有负权环则输出-1,若无法到达点 N则输出-2,否则直接输出1~N的距离即可。
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf (1LL<<60)
#define ll long long
using namespace std;

int n, ml, md, cnt;
int t[1005], last[1005], fa[1005], q[1005];
ll dis[1005];
bool inq[1005];
struct data{
	int to, next, v;
} e[40005];

int read()
{
    int x = 0, f = 1;
	char ch = getchar();
    while(ch < '0' || ch > '9')
	{
		if(ch == '-') f = -1;
		ch = getchar();
	}
    while(ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - '0';
		ch = getchar();
	}
    return x * f;
}

void insert(int u,int v,int w)
{
	e[++cnt].to = v; e[cnt].next = last[u]; last[u] = cnt; e[cnt].v = w;
}

int spfa()
{
	for(int i = 1; i <= n; i++) dis[i] = inf;
	int head = 0, tail = 1;
	q[0] = 1; t[1]++; dis[1] = 0;
	while(head != tail)
	{
		int now = q[head]; inq[now] = 0; head++;
		if(head == 1002) head = 0;
		for(int i = last[now]; i; i = e[i].next)
			if(dis[now] + e[i].v < dis[e[i].to])
			{
				t[e[i].to]++;
				if(t[e[i].to] == n + 1) return 0;
				dis[e[i].to] = dis[now] + e[i].v;
				if(!inq[e[i].to])
				{
					inq[e[i].to] = 1;
					q[tail++] = e[i].to;
					if(tail == 1002) tail = 0;
				}
			}
	}
	return 1;
}

int main()
{
	freopen("layout.in", "r", stdin);
	freopen("layout.out", "w", stdout);
	n = read(); ml = read(); md = read();
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1; i <= ml; i++)
	{
		int a = read(), b = read(), d = read(); d = abs(d);
		insert(a, b, d);
	}
	for(int i = 1; i <= md; i++)
	{
		int a = read(), b = read(), d = read(); d = abs(d);
		insert(b, a, -d);
	}
	if(!spfa()) puts("-1");
	else if(dis[n] == inf) puts("-2");
	else printf("%lld\n",dis[n]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值