弱校-联盟 day2

版权声明:QAQ https://blog.csdn.net/priesty_/article/details/82960230
  • 怀着国庆假都过不好,被拉去考试的深深怨念,我还是来写总结了……orz

T1 购物 shopping

题目描述

货架上有 N  种不同的商品,第 i  种商品质量为 ai  。

在购买至少一件商品的购买方案中,商品质量和第 K  小的购买方案的质量和是多少。

两种购买方案不同当且仅当选择购买的商品是不同的。

【输入格式】

输入文件名为 shopping.in。

第一行两个用空格分离的正整数 N  和 K。

       第二行两 N  个用空格分离的正整数,第 i 个数表示 ai 。

 

【输出格式】

输出文件名为 shopping.out。

输出共一行,一个正整数,表示商品质量和第 K 小的购买方案的质量和。

【输入输出样例 1】

shopping.in

shopping.out

5 4

5 4 1 2 3

3

 

这道题……dalao说想到了正解还是比较简单,可惜比赛时我没想到……orz

巨菜QAQ

正解:先二分答案,然后暴力搜索验证,搜索枚举出来的元素超过K个就直接退出。

时间复杂度:KlogN

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxn=200000+50;

LL t[maxn],sum;
int a[maxn],n,tp,k;

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

bool check(LL x) 
{
	LL cur = 0;
	t[tp=1] = 0;
	for (int i=1;i<=n;i++) 
	{
		while (tp > 0 && t[tp]+a[i] > x) tp--;
		if (!tp) return 0;
		for (int j=1;j<=tp;j++) t[j+tp] = a[i] + t[j], cur++;
		if (cur >= k) return 1;
		tp <<= 1;
		sort(t+1,t+tp+1); 
	}
	return 0;
}

void solve()
{
	sort(a+1,a+n+1);
	LL L = 1, R = sum;
	while (L < R) 
	{
		LL mid = (L+R) >> 1;
		if (check(mid)) R = mid;
		else L = mid+1;
	}
	printf("%lld\n",L);
	return ;
}

int main()
{
	n=read(),k=read();
	for (int i=1;i<=n;i++) a[i]=read(), sum+=a[i];
	solve();
	
	return 0;
}

 

T2 排列交换swap

题目描述

给定一个长度为n的排列,以及一个正整数K。

你可以进行如下操作若干次使得排列的字典序尽量小。

对于两个满足|i-j|≥K且|Pi-Pj|=1的下标i和j,交换Pi和Pj。

 

【输入格式】

输入文件名为 swap.in。

第一行两个用空格分离的正整数N和K。

第二行两N个用空格分离的正整数,第i个数表示Pi 。

 

【输出格式】

输出文件名为 swap.out。

输出共N行,输出排列字典序最小的排列,每行一个整数表示新排列的第i项。

 

【输入输出样例 1】

swap.in

swap.out

4 2

4 2 3 1

2

1

4

3

题意很好理解,第一反应就想暴力……

好吧,看一眼数据范围,还是老老实实一点吧

正解:读入 p[i],转化为: q[p[i]]=i,q[i]表示值为i的位置在哪。

考虑对于q[i]和q[j],若 |q[i]-q[j]|<k,(相距小于k),则q[i]和q[j]始终不能互换位置。,于是q[i]向q[j](i>j)连一条有向边即可,这样就构成了一个有向无环图。

将入度为0的点加入一个小根堆,做一遍拓扑排序。倒着枚举i,每次从堆里取出一个编号最小的x,使 p[i]=x,再将x连出的点入度减一;为0,则加入堆中。最后使q[p[i]]=i, 再将q输出即可。

为什么要维护一个堆呢?因为如果两点连了边(不能位置互换),就不会同时存在于堆中。在队中的点,肯定是可以位置互换的,那肯定是将小的数填在位置小那里,才能保证字典序最小。

又由于一个点只需与离它最近的点连边,维护一个维护最小值的线段树,倒着枚举i,每次查询 [q[i]-k+1,q[i]]和 [q[i],q[i]+k-1]两个区间的最小值即距离最近的两个点(一大一小),连边后再将q[i]这个位置单点修改成编号i即可.

是8是很容易?蓝鹅我考试时数组只开了两倍,其实应该开4倍的……所以光荣的RE了,只拿了55otz

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;
//我选择死亡 

const int maxn=500000+5;

int n,k;
int tot=0;
int in[maxn];
int a[maxn],b[maxn];//p,q
int nxt[maxn*4],to[maxn*4];//^
int tree[maxn*4],head[maxn*4];

priority_queue<int,vector<int>,less<int> >q;
//堆 

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

void addedge(int u,int v)
{
	nxt[++tot]=head[u];
	head[u]=tot;
	to[tot]=v;
	in[v]++;
}

void build(int l,int r,int rt,int x,int y)
{
	if(l==r)
	{
		tree[rt]=y;
		return ;
	}
	int mid=(l+r)>>1;
    if(x<=mid) build(lson,x,y);
	else build(rson,x,y);
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}

int query(int l,int r,int rt,int x,int y)
{
	if(x<=l && r<=y) return tree[rt];
	int mid=(l+r)>>1;
	if(y<=mid) return query(lson,x,y);
	if(x>mid) return query(rson,x,y);
	return min(query(lson,x,mid),query(rson,mid+1,y));
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) b[a[i]=read()]=i;;
	memset(tree,60,sizeof(tree));
	for(int i=n;i;i--)
	{
		int x=query(1,n,1,b[i]-k+1,b[i]);
		if(x<=n) addedge(b[x],b[i]);
		int y=query(1,n,1,b[i],b[i]+k-1);
		if(y<=n) addedge(b[y],b[i]);
		
        build(1,n,1,b[i],i);
	}
	for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
	for(int i=n;i;i--)
	{
		a[i]=q.top();
		q.pop();
		for(int j=head[a[i]];j;j=nxt[j])
		{
			int v=to[j];
			if(!--in[v])
			{
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n;i++) b[a[i]]=i; 
	for(int i=1;i<=n;i++) printf("%d\n",b[i]);

	return 0;
}

T3 收益 petrol

概率dp;

随便搞一下就好了;

正解:

设f[x]和g[x]表示用户给x元的概率以及期望的分红。

那么显然答案就是\large \sum {_{x<=L}}f[ x] \right ]*M-g\left [ x \right ];

算f[x]和g[x]的时候,我们可以用背包dp,假设f[i][x],g[i][x]表示考虑了前i个人用户给了x元,对应的概率与期望分红。那么有

f[i][x]=f[i-1][x-m[i]]*p[i]+f[i-1][x]*(1-p[i])

g[i][x]=(g[i-1][x]*(1-p[i])+(g[i-1][x-m[i]]+f[i-1][x-m[i]]*m[i]*(r[i]+1)))*p[i]

我自己那份代码太丑了,没好意思拿上来放,就传了一份老师发的标程

#include<bits/stdc++.h>
using namespace std;
#define MAXN 105
#define ll long long
#define P 1000000007
int n,m,x,y,i,j,ans,a[MAXN],b[MAXN],c[MAXN],f[500005],g[500005];
int main()
{
    scanf("%d%d%d",&n,&x,&y);
    for(i=1;i<=n;i++)
    {
        scanf("%d%d%d",a+i,b+i,c+i);
        b[i]=b[i]*570000004LL%P;
        c[i]=c[i]*570000004LL%P;
        m+=a[i];
    }
    for(f[0]=i=1;i<=n;i++)for(j=m;~j;j--)if(j>=a[i])
    {
        g[j]=((ll)(P+1-c[i])*g[j]+(g[j-a[i]]+(ll)f[j-a[i]]*a[i]%P*(b[i]+1))%P*c[i])%P;
        f[j]=((ll)(P+1-c[i])*f[j]+(ll)c[i]*f[j-a[i]])%P;
    }
    else
    {
        g[j]=(ll)(P+1-c[i])*g[j]%P;
        f[j]=(ll)(P+1-c[i])*f[j]%P;
    }
    for(i=x;i<=m;i++)ans=(ans+P-g[i]+(ll)f[i]*(y+i))%P;
    cout<<ans<<endl;
    return 0;
}

唉,昨天的还没总结……QAQ事情贼多

这次考试比较……嗯,不尽人意,小bug很多,像今天数组开小了丢了一百多分都是很不应该的

顺便膜一下AK的付大佬%%%%%%

猜你喜欢

转载自blog.csdn.net/priesty_/article/details/82960230