ACM International Collegiate Programming Contest (2018)A~F

梦想与她 提交于 2020-02-17 01:57:58

ACM International Collegiate Programming Contest (2018)A~F

A. Careful Thief

给出n个l,r,v表示在l到r范围内的每一个点价值为v
你可以得到连续的一段区间的总价值,区间最长为k,求可以得到的最大值。
对于这个问题,答案只在两种情况下产生:
1,起始位置为各个区间的左端点,结束位置为起始+长度-1;
2,结束位置为各个区间的右端点,起始位置为结束-长度+1;
对于1只需要每次枚举左端点,算出右端点,把中间的加起来就好了,然后维护最大值。
对于2也类似。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
struct zj
{
    LL l,r,val;
}ans[200055];
LL sum[200055],cun[200055],cun2[200055];
bool cmp(zj a,zj b)
{
    return a.l<b.l;
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
	{
        LL m,k;scanf("%lld%lld",&m,&k);
        for(int i=1;i<=m;++i)
		{
			scanf("%lld%lld%lld",&ans[i].l,&ans[i].r,&ans[i].val);
		}
        sort(ans+1,ans+1+m,cmp);
        for(int i=1;i<=m;++i)
		{
            sum[i]=sum[i-1]+ans[i].val*(ans[i].r-ans[i].l+1);
            cun[i]=ans[i].r;
            cun2[m-i+1]=-ans[i].l;
        }
        LL asd=0,curr,curl;
        for(int i=1;i<=m;i++)
		{
			curr=ans[i].l+k-1;
            int nexo=lower_bound(cun+1,cun+1+m,curr)-cun;
            LL lin=sum[nexo-1]-sum[i-1];
            LL value=ans[nexo].val;
            if(nexo<=m)
			lin+=(max(0LL,min(curr,cun[nexo])-ans[nexo].l+1))*value;
            asd=max(lin,asd);
        }
        for(int i=m;i>=1;--i)
		{
			curl=-ans[i].r+k-1;
            int nexo=lower_bound(cun2+1,cun2+1+m,curl)-cun2;
            LL lin=sum[i]-sum[m-nexo+1];
            LL value=ans[m-nexo+1].val;
            if(nexo<=m)
			lin+=max(0LL,(min(curl,cun2[nexo])-(-ans[m-nexo+1].r)+1))*value;
            asd=max(lin,asd);
        }
        cout<<asd<<endl;
    }
    return 0;
}

B. Friends and Cookies

总共有a快饼干给n个朋友(编号1~n)分饼干,顺序为(1->…-> n-1 -> n -> n-1 ->…->1)问每个朋友得到多少饼干。
显然,每2*n-2 个人为一个周期,剩下的最后一轮模拟即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
LL a[1111];
int main()
{
	int T=read();
	while(T--)
	{
		memset(a,0,sizeof(a));
		LL x=read();LL n=read();
		if(n==1)
		{
			cout<<x<<endl;continue;
		}
		LL co=x/(n*2-2);x=x%(n*2-2);
		a[1]=a[n]=co;
		for(int i=1;i<=n&&i<=x;++i)a[i]++;x-=n;
		for(int i=x,o=n-1;i>=1;--i,o--)a[o]++;
		for(int i=1;i<=n;++i)
		{
			if(i!=1&&i!=n)a[i]+=co+co;
		}
		for(int i=1;i<=n;++i)cout<<a[i]<<" ";
		cout<<endl;
	}
}

C. Flip the Bits

二进制的数字从n到n-1要改变几位。
用lowbit取出最后一位按位取反就是答案,所以看他有几位就好了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
int lbi(int x)
{
	return x&(-x);
}
int main()
{
	int t=read();
	while(t--)
	{
		int n=read();
		n=lbi(n);int i=1,j=1;
		while(n>i){i<<=1;j++;}
		cout<<j<<endl;
	}
}

D. Magic Sticks

首先明确一点,边的长度是1,也就是说这道题的边是最短的那个线段,而不是最长的…嗯
可以这样取:只取竖着的边,第一行与第二行间的边取第二个然后隔一个取一个。第二行与第三行间取第一个,然后交替。对于每一行交替进行前两次的操作。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
int main()
{
	int T=read();
	while(T--)
	{
		LL n,m;n=read();m=read();
		if(n>m)swap(n,m);
        cout<<n/2*(m/2+1)+(n+1)/2*(m-m/2)<<endl;
	}
}

E.N-Dimensional Grid

其实就是求
i=1n[a1a2(ai1)ai+1an] \sum_{i=1}^{n}[a_1*a_2*\ldots*(a_i-1)*a_{i+1}*\ldots*a_n]
令tot为所有数的积,所以只要求出
i=1n[(totai)/(ai1)] \sum_{i=1}^{n}[(tot*a_i)/(a_i-1)]
用到逆元求除法,每次乘以ai1a_i-1的逆元即可,可以用线性的逆元推法,也可以直接求

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
LL a[2200022];LL mod=1000000007;
LL qc(LL a,LL b)
{
	LL kk=0;
	while(b)
	{
		if(b&1)kk=(kk+a)%mod;
		b>>=1;a=(a+a)%mod;
	}
	return kk;
}
void exgcd(LL a,LL b,LL &x,LL &y)
{
   if(!b){x=1;y=0;return;}
   exgcd(b,a%b,y,x);
   y-=(a/b)*x;
}
LL ni(LL a)
{
   LL nia,y;
   exgcd(a,mod,nia,y);
   return (nia%mod+mod)%mod;
}
LL ny[220001];
int main()
{
	ny[1]=1;
    for(int i=2;i<=200011;i++)ny[i]=((mod-(mod/i))*ny[mod%i])%mod;
	int T=read();
	while(T--)
	{
		LL n=read();LL tot=1,ans=0;
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;++i){a[i]=read();tot=(a[i]*tot)%mod;}
		for(int i=1;i<=n;++i)
		{
			LL lin=(ny[a[i]]*tot)%mod;
			if(a[i]>1)ans=(ans+((a[i]-1)*lin))%mod;
		}
		cout<<ans<<endl;
	}
}

F. Minimum Sum of Array

有一个长度为n的数组,对于数组中的两个元素x,y如果满足y%x==0,则可以将y转换成x,求经过多次变换后数组的和最小是多少
用vis并记录每一个数在数组里出现了几次,用筛法处理在最大值和最小值之间的所有数,找到后从总数里面减去差值,在将对应的vis清零表示已经处理过,因为是从小到大处理,所以第一次处理可以保证最优。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
int a[2000111],vis[2000111];LL tot;
int su[2000111],co=0;
int main()
{
	int T=read();
	while(T--)
	{
		memset(a,0,sizeof(a));
		memset(vis,0,sizeof(vis));
		tot=0;
		int n=read(),ma=-1;
		for(int i=1;i<=n;++i)vis[a[i]=read()]++;
		for(int i=1;i<=n;++i)tot+=a[i],ma=max(ma,a[i]);
		for(int i=1;i<=ma;++i)
		{
			if(vis[i])
			for(int j=2;i*j<=ma;++j)
			{
				if(vis[i*j])
				{
					tot-=(i*j-i)*vis[i*j];
					vis[i*j]=0;
				}
			}
		}
		cout<<tot<<endl;
	}
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!