题目大意:有n支球队,两两之间有一场比赛,我们记录两队的进球数,对于每一队,她们该场比赛的效益为自己队进球数-对手进球数,我们现在已知n-1支队伍的效益,需要求第n支队伍的效益。
思路:在一场比赛中,我们假设a队进了a个球,b队进了b个球,那么对于a队,该场比赛的效益为:w1=a-b,对b队:w2=b-a,故而w1+w2=0.我们进而推广可知所有队伍所有场的比赛累计下来的总效益为0,那么只需要用0减去n-1支队伍的效益和即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int ans=0;
for(int i=1;i<n;i++)
{
int x;
scanf("%d",&x);
ans += x;
}
ans = 0-ans;
printf("%d\n",ans);
}
}
题目大意:P是村长,现有一个消息需要通过他分享给n个居民,现在有两种分享策略:1.由P分享给其他村民,每分享一个花费为p,2:由村民分享给村民,对于村民i,他最多能分享ai次,每次的费用为bi,求完成分享的最小费用。
思路:首先,这题的最优解肯定是要每次分享的花费都最少。那么就要考虑bi和p的关系,如果bi的最小值大于等于p的话,那么肯定是有P分享给其他人更划算,如果有一部分bi小于P的话,肯定是由这部分人来分享更划算,另外还要注意一点,虽然这部分人中的第一个人需要P来通知,但是这些人之间可以互相通知,并不需要P通知这部分所有人。那么策略就出来了,我们先找出bi的最小值,判断它与p的关系,如果大于等于的话,那么最小费用就是p*n;如果小于的话,那么我们先用一个变量来记录当前有多少人被通知到了,然后去循环村民,如果当前村民的bi小于p,那么这个村名肯定是能够去通知其他人的,我们就要看这个村民有没有被通知到,因为有个记录多少人已经被通知的变量,同时,我们的遍历是按顺序的,所以将当前居民的编号和这个变量比较一下即可,如果没被通知,那么就需要P去通知它,然后记录人数的变量加上他可以通知的人的数量。一旦当前这个人的单次费用大于等于p的时候就没必要再继续循环了,直接让P去通知其他人即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct cm
{
int a,b;
}c[100010];
bool cmp(cm x,cm y)
{
return x.b<y.b;
}
signed main()
{
int t;
scanf("%lld",&t);
while(t--)
{
int n,p;
scanf("%lld%lld",&n,&p);
for(int i=1;i<=n;i++)
{
int x;
scanf("%lld",&x);
c[i].a=x;
}
for(int i=1;i<=n;i++)
{
int x;
scanf("%lld",&x);
c[i].b=x;
}
sort(c+1,c+1+n,cmp);
int ans=0;
if(c[1].b>=p) ans=n*p,printf("%lld\n",ans);
else
{
int r=0;
ans=0;
for(int i=1;i<=n;i++)
{
if(c[i].b<p)
{
if(r<i) ans += p,r++;
if(r+c[i].a<=n) r += c[i].a,ans += c[i].a*c[i].b;
else ans += (n-r)*c[i].b,r=n;
}
else break;
if(r==n)break;
}
//剩下的都由p来通知
ans += (n-r)*p;
printf("%lld\n",ans);
}
}
}
ps:是有爆int的可能的。
题目大意:现有一个空数组a[],其中元素从a[1]-a[n+1],我们需要将这个数组填满,我们能选择的就是将[0,m]中的某个数放到a[n+1]位置,然后a[i]=a[i+1]%i,数组自动被填满。现在我们要求数组最后只能有k种元素,问能否实现,如果可以实现的话有多少种选法。
思路:我们分析一下可以意识到,一旦有一个位置出现0,那么后面一定全都是0,分类讨论一下:
设a[n+1]=x,a[n]=k
a[1] a[2]...a[k]...a[n-1] a[n] a[n+1]
0 0 0 k k x(x>n,对n取模后,值会变化)
a[1] a[2]...a[k]...a[n-1] a[n] a[n+1]
0 0 0 x x x(k<x,取模后值不变)
a[1] a[2]...a[k]...a[n-1] a[n] a[n+1]
0 0 0 0 0 x(x==n,对n取模后值为0,另外n的倍数也可以实现这个)
a[1] a[2]...a[k]...a[n-1] a[n] a[n+1]
0 0 0 0 0 0(x==0)
我们可以发现只有这几种情况,所以实际上就讨论出来了。最多只能有三种数,如果要求的种类数>3直接判否,然后如果是3,那么就有m-n种选法,如果这个值小于等于0,那么就判否;如果是2,就有min(n,m)种选法;如果是1,只有一种选法。另外要注意倍数的事情,对于倍数来说,大于1倍的要被算进第二种情况。而且要注意考虑有没有,否则会减多。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
if(k>3) printf("0\n");
else
{
int ans=0;
if(k==3)
{
ans = m-n;
//要把倍数去掉
if(m>n) ans -= (m/n-1);
ans=max(0,ans);
}
else if(k==2)
{
ans =min(n,m);
//m有可能小于n,不能直接这么算,否则就会出负的情况
if(m>n) ans += (m/n-1);//要把一倍减掉
}
else
{
ans=1;
}
printf("%d\n",ans);
}
}
}
题目大意:我们给定一个大小为n的数组a[],我们可以选择若干个索引,将该点处染成黑色,然后该索引的倍数索引位置被染成绿色,这种选法的得分为所有黑色和绿色处的值的最大值,我们将所有选法的分数都计算出来求和。
思路:一定要把题目看清楚,每次的分数是最大值,而非和,两者具体到细节上的处理是截然不同的。首先我们预处理出来每个位置如果被选的话,产生的最大值。 实际上就是将从它和它的倍数中取一个最大值,有两种处理方法,一种就是直接进行循环,对每个i去找它的倍数,时间复杂度为nlogn,另一种就是从后往前访问,对于某一个i,如果它的倍数已经出现过,我们就用它的倍数来更新它,实际上跟前一种差不多,不过我这里引入了质数进行优化,我们只用它的质数倍来更新它(我们举个例子来证明正确性,2:4,6,8,...,这里8是2的四倍,8可以将4更新,另外从普遍的角度来考虑,我们对于一个数的合数倍,它一定可以从这个合数中拆出一个质数p,变成k*p倍,那么这个数的k倍肯定也会被这个合数倍更新,而且是先被更新,因为我们是从后往前访问的。)。然后我们来看既然是最大值,那么就要看它在多少中选择中可以作为最大值,对处理好的数组从大到小排序,对于最大的那个数,它可以在2^(n-1)种选择中做最大值,依次类推。
更新a[i]的两种方法:
1.循环
for(int i=1;i<=n;i++)//nlogn
for(int j=i;j<=n;j+=i)
a[i]=max(a[i],a[j]);
2.通过质数累计
vector<int>p;
st[N];
//p装奇数 1-100010
for (int i = 2; i <= n; i ++ )//线性筛质数
{
if (!st[i]) p.push_back(i);
for(auto it:p)
{
if(it>n/i) break;
st[it*i] = true;
if(i%it==0) break;
}
}
for(int i=n;i>=2;i--)
{
if(n-i>=i)//访问过i的倍数,只看奇数倍即可
{
s[i]=a[i];
for(auto it:p)
{
int tp=i*it;
if(tp>n) break;
else
{
s[i]=max(s[i],s[tp]);
}
}
}
else//没访问过i的倍数
{
s[i]=a[i];
}
}
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100010,mod=998244353;
int b[N],a[N];
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
b[0]=1;
b[1]=2;
for(int i=2;i<=N;i++) b[i]=b[i-1]*2,b[i]%=mod;
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)//nlogn
for(int j=i;j<=n;j+=i)
a[i]=max(a[i],a[j]);
sort(a+1,a+n+1,cmp);
int ans=0;
for(int i=1;i<=n;i++)
{
ans += (long long)a[i]*b[n-i]%mod;
ans %= mod;
}
printf("%d",ans);
}