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
其实就是求
令tot为所有数的积,所以只要求出
用到逆元求除法,每次乘以的逆元即可,可以用线性的逆元推法,也可以直接求
#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;
}
}
来源:CSDN
作者:backordinary
链接:https://blog.csdn.net/backordinary/article/details/104349163