标签:推出 idp ons set nlog max codeforce 匹配 比较
找出两个合数使得他们差为某给出的数n
先指定一个小的合数a,则b=a+n。若b是合数,则直接输出。若b是素数,则b+1一定不是素数。那么选一个a使得a,a+1均为合数即可。
#include<bits/stdc++.h>
using namespace std;
bool check(int x)
{
for(int i=2;i*i<=x;i++)
if(x%i==0)return 0;
return 1;
}
int main()
{
int n,a=8,b;
scanf("%d",&n);
b=a+n;
while(check(a)||check(b))
a+=1,b+=1;
printf("%d %d\n",b,a);
}
给出两个数列a,b,一个整数m,求一个最小的x,使得a中所有元素加x再模m之后与b相等。
要使得a最后与b相等,则a[0]必然会变成b中的一个元素,所以x的所有可能性一共2000种。所以O(n^2)的暴力测试所有可能性就好了。
#include<bits/stdc++.h>
using namespace std;
const int MAX=2005;
vector<int>p;
int a[MAX],b[MAX],c[MAX],n,m;
bool check(int x)
{
for(int i=0; i<n; i++)
c[i]=(a[i]+x)%m;
sort(c,c+n);
for(int i=0; i<n; i++)
if(c[i]!=b[i])return 0;
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
scanf("%d",&a[i]);
for(int i=0; i<n; i++)
scanf("%d",&b[i]);
sort(a,a+n);
sort(b,b+n);
for(int i=0; i<n; i++)
{
int aa=a[i],bb=b[0];
if(aa<bb)p.push_back(bb-aa);
if(aa>bb)p.push_back(bb+m-aa);
}
p.push_back(0);
sort(p.begin(),p.end());
unique(p.begin(),p.end());
for(int i=0; i<p.size(); i++)
if(check(p[i]))
{
printf("%d\n",p[i]);
break;
}
}
以数位的形式给出一个n位数a,再给出一个整数k,求一个最小的m位数b,使得b>a,并且对于任意i | 1<=i<=m-k,有b[i]==b[i+k]。
题意翻译一下就是b是由一个k位的数循环构成,尾部可以不足k位,所以只需要b的前k位大于等于a的前k位。要求b尽可能小,所以先用等于a的前k位构造b,再暴力比较一下,如果合法就输出,否则用a的前k位再加1构造,加一后可能存在进位,需要处理一下,但是不可能超过n位,因为999...这种情况一定能保证大于等于a。
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+5;
char a[MAX],b[MAX];
int main()
{
int n,k;
scanf("%d%d%s",&n,&k,a+1);
printf("%d\n",n);
for(int i=1; i<=k; i++)
b[i]=a[i];
for(int i=k+1; i<=n; i++)
b[i]=b[i-k];
bool flag=1;
for(int i=1; i<=n; i++)
{
if(b[i]>a[i])
break;
if(b[i]<a[i])
{
flag=0;
break;
}
}
if(flag)
{
printf("%s\n",b+1);
return 0;
}
for(int i=k;i>0;i--)
{
if(b[i]=='9')
b[i]='0';
else
{
b[i]++;
break;
}
}
for(int i=k+1; i<=n; i++)
b[i]=b[i-k];
printf("%s\n",b+1);
}
给出一个由1x1的正方形组成的图形,左右下平整。求这个图形中最多能填充多少个1x2的骨牌。
思维神题。貌似很dp,实际上只需要把这个图形涂成象棋棋盘一样的黑白格。设白色少于黑色(多于黑色交换一下就行)。则有:
则由3可知,res=min(b,w),再由1,2可知所有白色格子一定可以匹配到一个黑色格子。所以可以推出答案就是白色的格子个数。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,x;
long long b=0,w=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&x);
b+=(x/2);
w+=(x/2);
if(i&1)b+=(x&1);
else w+=(x&1);
}
printf("%I64d\n",min(b,w));
}
给出一个长度为n的排列p,每次操作可以交换相邻两数的位置。求对于k属于1-n的所有k,使得p中出现1-k的排列的最小操作次数(每个k独立)。
对于连续的排列,则很明显就是求逆序数,但是本题难点在于k<n时,排列可能是被隔开的。所以需要先凑到一起。凑的话一定是两边向中间凑操作数最小。假设现在有k个数需要凑到一起。设数i当前的位置为s[i],凑到一起后的位置为t[i],凑到一起需要的操作次数为sum。则有:
\[
\begin{equation}
\begin{aligned}
sum&=\sum_{1}^k|s{[i]-t[i]|}\\sum&=\sum_{s[i]>t[i]} s[i]-t[i]+\sum_{t[i]>s[i]}t[i]-s[i]\\\&=\sum_{right}s[i]-\sum_{right}t[i]+\sum_{left}t[i]-\sum_{left}s[i]\quad(1)
\end{aligned}
\end{equation}
\]
对于1式中中间两项,可以通过求出最中间的那个数的位置midpos来求出,左侧的t[i]一定是midpos-1,midpos-2......,右侧则为midpos+1,midpos+2......,则可以通过等差数列求和公式快速求出。而对于s[i],则可以通过树状数组维护,更新就在s[i]位置加s[i],这样求区间和就可以O(logn)的得到1式中的左右两项。
而对于midpos,可以用set来维护,根据新插入数据在之前midpos的左右来更新midpos,保证midpos始终合法。
所以总共用两个树状数组,一个维护逆序数,一个维护区间和,再用一个set维护midpos,总时间复杂度O(nlogn)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=2e5+5;
int a[MAX],p[MAX],n;
ll c1[MAX],c2[MAX];
set<int>st;
void add(ll *c,int x,int k)
{
for(;x<=n;x+=x&-x)
c[x]+=k;
}
ll query(ll *c,int x)
{
ll sum=0;
for(;x;x-=x&-x)
sum+=c[x];
return sum;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),p[a[i]]=i;
st.insert(p[1]);
auto it=st.begin();
printf("0 ");
add(c1,p[1],1);
add(c2,p[1],p[1]);
ll res=0;
for(int i=2;i<=n;i++)
{
st.insert(p[i]);
if(p[i]<(*it)&&i%2==0)it--;
if(p[i]>(*it)&&i%2==1)it++;//保证偶数时右侧始终多于左侧1个,以保证后面的正确性。
add(c1,p[i],1);
res+=(i*1ll-query(c1,p[i]));
add(c2,p[i],p[i]);
int midpos=*it;
ll sum=0,k=i/2;
sum+=i&1?k*(midpos-1+midpos-k)/2:(k-1)*(midpos+midpos-k)/2;
sum-=k*(midpos+1+midpos+k)/2;
sum-=query(c2,midpos-1);
sum+=query(c2,n)-query(c2,midpos);
printf("%I64d ",res+sum);
}
}
标签:推出 idp ons set nlog max codeforce 匹配 比较
原文地址:https://www.cnblogs.com/cryingrain/p/12431556.html