贪心。
对于每个数,找二进制形式下末尾连续1的数量,若为i,则可连到数字2^(i-1),若2^(i-1)不存在,则连到1最优。
AC代码:
using namespace std;
#include
int t,n,i,x;
int a[200001];
long long sum,ans;
int main()
{
scanf("%d",&t);
while (t!=0)
{
t--;
scanf("%d",&n);
for (i=2;i<=n;i++)
{
sum=1;
x=i;
while (x!=0)
{
if (x%2==0)
break;
else
{
sum=sum*2;
x=x/2;
}
}
if (sum>n)
a[i]=1;
else
a[i]=sum;
}
ans=0;
for (i=2;i<=n;i++)
ans+=a[i]&i;
printf("%lld\n",ans);
for (i=2;i<=n;i++)
if (i!=n)
printf("%d ",a[i]);
else
printf("%d\n",a[i]);
}
return 0;
}
题面很坑的少了n/k,导致最后才知道还得保证每组是n/k个石头。
显然无解的情况:
2、n==k且n!=1。
因为要分成k组,每组n/k个石头。
将n个石头等分为n/k个区间,每个区间都是连续k个值。
若n/k为偶数,则可每两个区间凑在一起,保持k组的差值始终为0即可。
若n/k为奇数,则k必为奇数,否则就会是显然无解的情况1。(n/k-3)是偶数,先两两之间就可以凑出为0的情况即可。然后考虑用剩下三个区间凑出相等的k组。
三个区间凑的情况可以试试数据15 5
然后就可以发现一种简单的构造方法,第一列从大到小,第二列按间隔为2的形式从小往大取(其实就是分奇偶),这样可以保证最后一列要取的数字不会重复,最后一列直接用单组区间和减去已经取了的区间和即可。
AC代码:
using namespace std;
#include
int t,x,i,j;
long long n,k;
vector a[100001];
long long sum[100005];
int main()
{
// freopen("in.txt","r",stdin);
scanf("%d",&t);
while (t)
{
t--;
scanf("%lld%lld",&n,&k);
if (n==1)
{
printf("yes\n1\n");
continue;
}
if ((n*(n+1)/2)%k!=0 || n==k)
{
printf("no\n");
continue;
}
printf("yes\n");
for (i=1;i<=k;i++)
a[i].clear();
x=n/k;
if (x%2==0)
{
for (i=1;i<=x;i++)
{
for (j=1;j<=k;j++)
if (i%2==1)
a[j].push_back((i-1)*k+j);
else
a[j].push_back((i-1)*k+(k-j+1));
}
}
else
{
for (i=1;i<=x-3;i++)
{
for (j=1;j<=k;j++)
if (i%2==1)
a[j].push_back((i-1)*k+j);
else
a[j].push_back((i-1)*k+(k-j+1));
}
//最后三列
for(int i=1;i<=k;i++) sum[i]=0;
for(int j=1;j<=k;j++) a[j].push_back((x-3)*k+k-j+1);
for(int j=1,z=(x-2)*k+1;z<=(x-1)*k;j++,z+=2) a[j].push_back(z);
for(int j=k/2+2,z=(x-2)*k+2;j<=k;j++,z+=2) a[j].push_back(z);
for(int i=0;i<=x-2;i++) for(int j=1;j<=k;j++) sum[j]+=a[j][i];
for(int j=1;j<=k;j++){
a[j].push_back(n*(n+1)/2/k-sum[j]);
}
}
for (i=1;i<=k;i++)
{
for (j=0;j
这一类问题似乎有一个判断是否能到最终状态的方法:根据逆序对个数和空位到最终位置的曼哈顿距离的和是否是偶数,若为偶数则有解;否则无解。
然后题解说120步一定能到终点。不是我写的,不太清楚。
似乎是cys先对wtw说可以主席树,wtw:“你不要用这种垃圾算法来糊弄我。”(???
然后wtw写了二分+主席树,结果第一发WA,第二发T(此时比赛还剩一个半小时),后面又交了17发,基本都是T,还有几发WA。最后一小段时间他T到没办法了,准备改成离散化,没改完,到最后也没过。
赛后发现第二发那个T就能AC,后面很多发T也都是能AC的。。。(心疼,多自闭一个半小时。。。
具体解法等他写博客。
考虑答案大于1的情况。
对于可表示为x^k的数,很显然我们可以很容易得到答案(比如枚举k,然后二分或者通过pow函数得到x)。
除了上述情况,要想答案为2,则至少含有两种素因子,且较小幂次为2。假设两个数分别为p,q,,若,则显然有。
因此,考虑先打出4000以内的素数表,然后枚举素数表分解n,得到分解部分和未分解部分m,令,ans1为m的答案,则最后答案为min(ans0,ans1)。
现在只需要考虑m。首先可以知道,m的最小素因子大于4000,则可知m不为1的情况可能为、、、。
先判m是否可表示为x^2
如果可以,再看x是否可表示为y^2,若可以,则ans1=4;否则ans1=2。
如果不可以,看x是否可表示为x^3,若可以,则ans1=3;否则ans1=1。
最后得到答案min(ans0,ans1)。
AC代码:
#include
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mst(a,b) memset(a,b,sizeof(a))
const int maxn=4e3+5;
const db eps=1e-8;
const ll INF=1e18;
const int mod=1e9+7;
const int seed=131;
int t;
ll x;
int prime[maxn],tot;
bool notprime[maxn];
void init(){
notprime[1]=1;
for(int i=2;i