Alsing Programming Contest 2020 A-D题解

A

Solution

直接枚举即可。时间复杂度 O ( [ r l d ] ) O([\frac {r-l} d])

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

int d,l,r;

signed main()
{
	cin>>l>>r>>d;
	
	if (l%d!=0)  l=l+(d-l%d);
	r=(r/d)*d;
	
	cout<<((r-l)/d)+1<<endl;
	
	return 0;
}//数学解法

B

Solution

直接模拟即可。

时间复杂度 O ( n ) O(n)

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n;
int a[20005];

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  cin>>a[i];
	
	int tot=0;
	for (int i=1;i<=n;i++)
	{
		if (i%2==1&&a[i]%2==1)  tot++;
	 } 
	
	cout<<tot<<endl;
	
	return 0;
}

C

Solution

直接打表即可。注意尽量控制计算机上打表的时间,并利用这段时间思考别的题目

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;



signed main()
{
	int n;
	cin>>n;
	
	for (int i=0;i<n;i++)  cout<<ans[i]<<endl;
}

D

Solution

比较神仙的一道题目(虽然本蒟蒻做出来了),有思维含量和很多卡点。

首先,考虑一下朴素做法。我们每次枚举到 i i ,将 i i 翻转然后用高精度暴力求出当前二进制数表示的十进制数,然后求出它的 f f 值即可。

显然此时会TLE。那么我们该怎么办呢?


优化1: 去除重复计算!

先求出读入的二进制数所表示的十进制数 k k

枚举到 i i 时,若 a i a_i 0 0 那么应当把 a i a_i 变为 1 1 ,此时 k k 加上了第 i i 位的权值即 2 n i 2^{n-i} ;若 a i a_i 为1,那么同理, k k 减去了第 i i 位的权值即 2 n i 2^{n-i}

所以,我们每次不需要暴力地翻转然后求其表示的十进制数,我们只需要按上述方式加或减一个值即可。

优化2: 去除高精计算!

假设我们通过优化 1 1 得到了当前翻转之后表示的十进制数为 k k 。无论如何,经过第一步 ( k : = k   m o d   p o p c o u n t ( k ) ) (k:=k\ mod\ popcount(k)) k k 就立即变成了一个可以用 i n t int 存下的一个数( p o p c o u n t ( k ) popcount(k) 小于等于 n n )。于是,我们考虑直接求出第一步之后的 k k

假设读入的 01 01 串中有 x x 个1。同样是两种情况:
①若 i i 0 0 ,那么翻转后 1 1 的数量为 x + 1 x+1 ,此时在第一步之后 k : = ( ( k + 2 n i )   m o d   ( x + 1 ) ) k:=((k+2^{n-i})\ mod\ (x+1))
②若 i i 1 1 ,那么翻转后 1 1 的数量为 x 1 x-1 ,此时在第一步之后 k : = ( ( k 2 n i )   m o d   ( x 1 ) ) k:=((k-2^{n-i})\ mod\ (x-1))

于是,我们可以对于每个模数 ( x + 1 x 1 ) (x+1和x-1) 分别求出 k k 的值,然后就可以快速地求出答案并取消高精度计算了。

保守地一下时间复杂度: O ( n l o g 2 n l o g 2 n ) O(nlog_2nlog_2n)


于是,我们开始不停地获得罚时 5 5 分的礼包。显然您肯定是因为 R E RE W A WA 而被罚时的。

这里给大家 4 4 H a c k Hack 数据的输入数据,基本保证能够找出问题

3
100

1
0

1
1

6
000000

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
int mod1,mod2;

int n,tot=0,pos1=1,pos2=1,tot1=0,tot2=0;
int a[200005];

inline int Popcount(int x)
{
	if (x==0)  return 0;
	
	int step=0;
	while (x!=0)
	{
		int cnt=0,k=x;
		while (k!=0)
		{
			if (k%2==1)  cnt++;
			k/=2;
		}
		if (cnt!=0)  x%=cnt;
		else x=0;
		
		step++;
	}
	return step;
}

int quick_power1(int x,int y)
{
	int res=1;
	for (;y;y=y>>1,x=(x*x)%mod1)
	{
		if (y&1)  res=(res*x)%mod1;
	}
	return res;
}

int quick_power2(int x,int y)
{
	int res=1;
	for (;y;y=y>>1,x=(x*x)%mod2)
	{
		if (y&1)  res=(res*x)%mod2;
	}
	return res;
}

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		char x;
		cin>>x;
		a[i]=x-'0';
	}
	for (int i=1;i<=n;i++)
	{
		if (a[i]==1)  tot++;
	}
	int flag=1;
	for (int i=1;i<=n;i++)
	{
		if (a[i]!=0)
		{
			flag=0;
			break;
		}
	}
	if (flag==1)
	{
		for (int i=1;i<=n;i++)  cout<<1<<endl;
		return 0;
	}
	mod1=tot-1;
	mod2=tot+1;
	
	for (int i=n;i>=1;i--)
	{
		if (mod1!=0)  tot1=(tot1+a[i]*pos1)%mod1,pos1=(pos1*2)%mod1;
		if (mod2!=0)  tot2=(tot2+a[i]*pos2)%mod2,pos2=(pos2*2)%mod2;
	}
	for (int i=1;i<=n;i++)
	{
		if (a[i]==0)
		{
			int now;
			if (mod2>0)
			{
				now=(tot2+quick_power2(2,n-i))%mod2;
				cout<<Popcount(now)+1<<endl;
			}
			else cout<<0<<endl;
		}
		else
		{
			int now;
			
			if (mod1>0)
			{
				now=((tot1-quick_power1(2,n-i))%mod1+mod1)%mod1;
				cout<<Popcount(now)+1<<endl;
			}
			else cout<<0<<endl;
		}
	}
	return 0;
}

E

考试的时候以为E比D难,实则不然

Solution

我们先把骆驼分成两份,第一份是喜欢在前面的(即 L i > R i L_i>R_i ),第二份是不喜欢在前面的(即 L i R i L_i≤R_i )。

显然第一份与第二份骆驼分别是独立的,即互相不影响。下面介绍一下第一份骆驼的摆放思路,第二份的摆放思路相同。

我们按照 K K 值从小到大进行排序。当看到这个骆驼的时候,我们看一下它是否有位置;如果有的话就直接把它插入队列并计算相应的贡献。如果没有的话,我们贪心地尝试用它换掉占它位置且目前最差(即对答案贡献更少)的骆驼。如果不能换(即该骆驼比之前最差的骆驼还要差)就扔掉这匹骆驼,否则就用它换掉目前最差的骆驼。

于是,我们每次只需要找到最差的骆驼,比较之后决定是否交换即可。

时间复杂度 O ( n 2 ) O(n^2)

这么做显然会超时。考虑优化。

可以发现,刚才想到的朴素做法中,我们每次都把所有安定下来的骆驼给扫一遍,找出最差的骆驼。

这是不必要的。由于每次最多插入一只骆驼(扫到的那个骆驼进入队列或不进入此队列),那么我们就可以直接使用优先队列来优化。

时间复杂度 O ( n l o g 2 n O(nlog_2n )。

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

int T,n,posa=0,posb=0,k,l,r,ans;
priority_queue<int,vector<int>,greater<int> >q1, q2;

struct node
{
    int L,R,K;
}A[200005],B[200005];

bool cmp(node x,node y)
{
	return x.K<y.K;
}

inline void clear()
{
	posa=posb=0;
    ans=0;
}

signed main()
{
    cin>>T;
    while (T--)
    {
        cin>>n;
        for (int i=1;i<=n;i++)
        {
            cin>>k>>l>>r;
            if (l>r)  A[++posa].L=l,A[posa].R=r,A[posa].K=k;
            else B[++posb].L=l,B[posb].R=r,B[posb].K=n-k;
            ans+=min(l,r);
        }
        sort(A+1,A+1+posa,cmp);
		sort(B+1,B+1+posb,cmp);
		
        for (int i=1;i<=posa;i++)
        {
            if (A[i].K==0)  continue;
            else if (q1.size()<A[i].K)  q1.push(A[i].L-A[i].R);
            else if (q1.size()==A[i].K&&q1.top()<A[i].L-A[i].R)
            {
            	q1.pop();
				q1.push(A[i].L-A[i].R);
			}
        }
        for (int i=1;i<=posb;i++)
        {
            if (B[i].K==0)  continue;
            if (q2.size()<B[i].K)  q2.push(B[i].R-B[i].L);
            else if (q2.size()==B[i].K&&q2.top()<B[i].R-B[i].L)
            {
            	q2.pop();
				q2.push(B[i].R-B[i].L);
			}
        }
        while (!q1.empty())
        {
        	ans+=q1.top();
        	q1.pop();
		}
        while (!q2.empty())
        {
        	ans+=q2.top();
        	q2.pop();
		}
        cout<<ans<<endl;
    }
    return 0;
}

总结&&反思

D D 题中,没有考虑边界数据导致出现两次 P e n a l t y Penalty 。应该在做题之后思考边界数据,然后 H a c k Hack 自己几次,确保 A C AC 之后再提交。

D D 题AC后彻底疯掉,导致无法静下心来思考 E E 题。如果是刷题刷到了 E E 题,那么 A C AC 还是有指望的;绝对不能满足,不能再出现比赛结束前咕咕咕鸽子飞走的事情(除非 A K AK )。

C C 题一直没反应过来打表,浪费了 7 7 分钟时间。

太菜了QWQ QAQ 喵~~~

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/107291888
今日推荐