A: Anti-knapsack
Description
选出一个集合 { 1 , 2 , 3 , … … , n } \left\{ 1,2,3,……,n \right\} {1,2,3,……,n}的子集,使得该子集中没有子集的元素和为 k k k,同时还要该使得该子集元素的个数最大化。
Solution
因为 1 < = k < = n 1<=k<=n 1<=k<=n,所以集合 { 1 , 2 , 3 , … … , n } \left\{ 1,2,3,……,n \right\} {1,2,3,……,n}中的 { k + 1 , k + 2 , k + 3 , … … , n } \left\{ k+1,k+2,k+3,……,n \right\} {k+1,k+2,k+3,……,n}是可以全部选出的,这些数的个数为 n − k n-k n−k。
令 t = ⌈ k 2 ⌉ t=\left \lceil \frac{k}{2} \right \rceil t=⌈2k⌉,然后根据手动模拟可以看出,下一步要取的数是 { t , t + 1 , t + 2 , t + 3 , … … , k − 1 } \left\{t,t+1,t+2,t+3,……,k-1 \right\} {t,t+1,t+2,t+3,……,k−1},这些数的个数为 ⌊ k 2 ⌋ \left \lfloor \frac k 2 \right \rfloor ⌊2k⌋
最后的总数 c n t = n − k + ⌊ k 2 ⌋ \quad cnt=n-k+\left \lfloor \frac k 2 \right \rfloor cnt=n−k+⌊2k⌋
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool vis[1005];
int main()
{
int t;cin>>t;
while(t--)
{
int n, k;
cin>>n>>k;
int cnt = n-k+k/2; //直接计数
cout<<cnt<<endl;
if(cnt<=0)
continue;
for(int i=n;i>=1;i--)
{
if(i>k)
cout<<i<<" ";
else if(i<k)
{
if(k%2==0 &&i>=k/2)
cout<<i<<" ";
if(k%2==1 &&i>k/2)
cout<<i<<" ";
}
}
cout<<endl;
}
return 0;
}
B:Planet Lapituletti
比较直接的模拟题,看懂题意后,直接模拟即可
注意 h h hh hh和 m m mm mm要进位,不过看到的镜像时间是啥就是啥,不要自己去进位(傻~~
成功条件很简单 i f ( n e w h < h a n d n e w m < m ) if(new_h<h\quad and\quad new_m<m) if(newh<handnewm<m)成立即可
注意前导0的输出,应该是可以一行给出的
printf("%02d:%02d",hh,mm);
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int tmp[12]={0,1,5,-1,-1,2,-1,-1,8,-1};
int f[120];
int main()
{
for(int i=0;i<=9;i++)
{
for(int j=0;j<=9;j++)
{
int now = i*10+j;
if(tmp[i]==-1||tmp[j]==-1)
{
f[now] = -1;
continue;
}
int rev = tmp[j]*10+tmp[i];
f[now] = rev;
}
}
int t; cin>>t;
while(t--)
{
int h, m;
cin>>h>>m;
int hh,mm;
scanf("%d:%d",&hh,&mm);
while(1)
{
if(mm==m)
mm=0,hh++;
if(hh==h)
hh=0,mm=0;
if(f[hh]==-1 || f[mm]==-1){
mm++;continue;
}
int n_hh = f[mm], n_mm = f[hh];
if(n_hh>=h || n_mm>=m){
mm++;continue;
}
if(n_hh<h && n_mm<m)
{ //看到的镜像时间 自己肯定不会去进位的呀!!!
if(hh<10)
cout<<"0"<<hh<<":";
else
cout<<hh<<":";
if(mm<10)
cout<<"0"<<mm<<endl;
else
cout<<mm<<endl;
break;
}
}
}
return 0;
}
D:GCD of an Array
遇到求 g c d gcd gcd的题,基本上是不用写求 g c d gcd gcd的函数的,因为如果写了,必然是暴力求解~~
所以最大公约数就得考虑质因数分解
- n n n个数的最大公约数是他们公有的质因数的最小幂次的乘积
- 考虑用筛法筛出数据范围内的所有质因数
- 考虑用 m a p map map维护每个 a [ i ] a[i] a[i]的质因数的幂次 ( 由 a [ i ] 的 范 围 可 知 质 因 数 并 不 多 ) (由a[i]的范围可知质因数并不多) (由a[i]的范围可知质因数并不多)
- 用 m u l t i s e t multiset multiset维护每个质因数在每一个 a [ i ] a[i] a[i]中出现的次数,只有当某个质因数的 m u l t i s e t multiset multiset中的个数为n时,该质因数才对最后的 g c d gcd gcd有贡献
- 现在考虑每一个 q u e r y query query,给某一个 a [ i ] a[i] a[i]乘上一个 x x x,所以可以将 x x x进行分解质因数,对于得到的每个质数 p p p的次数 c n t cnt cnt,如果 p p p没有在 a [ i ] a[i] a[i]中出现过,则直接插入到 m u l t i s e t multiset multiset中;如果出现过,则记录下 n u m = m p [ i ] [ p ] num = mp[i][p] num=mp[i][p],在 m u l t i s e t multiset multiset中找到 n u m num num并删掉,插入 c n t cnt cnt。
- 然后判断 m u l t i s e t . s i z e ( ) = = n ? multiset.size()==n? multiset.size()==n?,如果成立,则找到质数 p p p的最小幂次(注意要考虑上一次的贡献,做差),对 a n s ans ans累乘即可(优化快速幂与否均可)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans = 1;
ll mod = 1000000000+7;
const int N = 200000+10;
int n, q, nxt[N];ll a[N];
map<int,int>mp[N];
multiset<int>mt[N];
void add(int id,int x)
{
while(x!=1)
{
int div = nxt[x], cnt = 0, mi = 0, st;
while(nxt[x] == div)
{
x/=div;
cnt++;
}
int i = div;
int last = mp[id][i];
if(last==0)
{
mt[i].insert(cnt);
}
else
{
mi = *mt[i].begin();
multiset<int>::iterator pos = mt[i].find(last);
mt[i].erase(pos);
mt[i].insert(cnt+last);
}
st = *mt[i].begin();
mp[id][i] = cnt+last;
if(mt[i].size()==n)
{
for(int j=1;j<=st-mi;j++)
ans = (ans*(ll)i%mod)%mod;
}
}
}
int main()
{
// add(2,4*9*25*49);
ios_base::sync_with_stdio(0);
cin>>n>>q;
for(int i=2;i<N;i++)
{
if(nxt[i]==0)
{
nxt[i] = i;
if(i>10000) continue;
for(int j = i*i;j<N;j+=i)
if(nxt[j]==0)
nxt[j] = i;
}
}
// for(int i=1;i<=1000;i++)
// cout<<i<<" "<<nxt[i]<<endl;
for(int i=1;i<=n;i++)
{
cin>>a[i];
add(i,a[i]);
// cout<<ans<<endl;
}
while(q--)
{
int i, x;
cin>>i>>x;
add(i,x);
cout<<ans<<endl;
}
return 0;
}