利用等比数列求和得到等式 x ( 2 k − 1 ) = n x(2^k-1)=n x(2k−1)=n即 x = n 2 k − 1 x=\frac{n}{2^k-1} x=2k−1n,之后依次枚举k直到 ( 2 k − 1 ) ∣ n (2^k-1)|n (2k−1)∣n,相除即得到答案x
#include
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
int k=2;
while(1){
long long kk=k*2-1;
if(n%(kk)==0){
printf("%d\n",n/kk);
break;
}
k*=2;
}
}
return 0;
}
要求构造一个数字均不同的正数列,前 n / 2 n/2 n/2项为偶数,后 n / 2 n/2 n/2项为奇数,且偶数和与奇数和相等。当 n / 2 n/2 n/2为奇数时无解,因为奇数个偶数和为偶数,奇数个奇数和为奇数,所以两个和不可能相等。 n / 2 n/2 n/2为偶数时的思路:偶数直接放2,4,6,8…奇数放1,3,5,…最后一个奇数=偶数和-已放的奇数的和
#include
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
if((n/2)&1) printf("NO\n");
else{
long long sum1=0,sum2=0;
printf("YES\n");
for(int i=1;i<=n/2;i++){
printf("%d ",i*2);
sum1+=i*2;
}
for(int i=1;i<=n/2-1;i++){
printf("%d ",i*2-1);
sum2+=i*2-1;
}
printf("%lld\n",sum1-sum2);
}
}
return 0;
}
称一段连续的同号的数为一个块,从左到右遍历这些块,每次取块的最大值即可
#include
using namespace std;
const int N=200005;
long long a[N];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
long long sum=0;
long long maxx=-1e15;
for(int i=1;i<=n;i++){
bool ok=0;
if((a[i]>=0&&a[i-1]>=0||a[i]<=0&&a[i-1]<=0)){
maxx=max(maxx,a[i]);
}
else{
ok=1;
sum+=maxx;
maxx=-1e15;
maxx=max(maxx,a[i]);
}
}
sum+=maxx;
printf("%lld\n",sum);
}
return 0;
}
对于每一对数 a i a_i ai和 a n − i + 1 a_{n-i+1} an−i+1,为了使 a i + a n − i + 1 = x a_i+a_{n-i+1}=x ai+an−i+1=x,它的替换情况有三种:第一是两个数都被替换,第二是仅替换其中一个数,第三是不进行替换。对于每对数,我们可以算出最多进行一次替换的 x x x的范围,即 [ 1 + m i n { a i , a n − i + 1 } , k + m a x { a i , a n − i + 1 } ] [1+min\{a_i,a_{n-i+1}\},k+max\{a_i,a_{n-i+1}\}] [1+min{ai,an−i+1},k+max{ai,an−i+1}]也就是说当x的范围落在这个闭区间时,这对数最多只被替换一次,其中包括替换一次和不被替换的情况。我们再用 c n t x cnt_x cntx数组来记录和为 x x x的数对的数量。我们用 n u m x num_x numx记录当和为 x x x时,最多替换一次的总次数。那么对每对数进行处理时,就会发现这是个区间修改,单点查询的问题,但是查询只在最后进行一次查询,所以可以用差分的思想。令 p r e f x pref_x prefx记录 n u m x − n u m x − 1 num_x-num_{x-1} numx−numx−1,则对 n u m num num的区间 [ l , r ] [l,r] [l,r]进行修改(每个+1)时,只需让 p r e f l 加 1 pref_l加1 prefl加1和 p r e f r + 1 减 1 pref_{r+1}减1 prefr+1减1即可。然后计算 n u m i = ∑ j = 1 i p r e f j num_i=\sum_{j=1}^ipref_j numi=∑j=1iprefj。最后计算答案,公式在代码中体现
#include
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,k;
scanf("%d %d",&n,&k);
vectora(n+5,0),cnt(2*k+5,0),pref(2*k+5,0);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n/2;i++){
cnt[a[i]+a[n-i+1]]++;
int l1=1+a[i],r1=k+a[i];
int l2=1+a[n-i+1],r2=k+a[n-i+1];
pref[min(l1,l2)]++;
pref[max(r1,r2)+1]--;
}
for(int i=2;i<=2*k;i++){
pref[i]+=pref[i-1];
}
int ans=1e9;
for(int i=2;i<=2*k;i++){
ans=min(ans,pref[i]-cnt[i]+(n/2-pref[i])*2);
}
printf("%d\n",ans);
}
return 0;
}
路程可分为4段,a->x,x->b,b->x,x->c,可以发现{b,x}这段路走了两次,所以把最少的价格分配到这三条路上,其中{b,x}这段路分配最少的价格(中的最小价格)。通过3次BFS算出a,b,c到各个点的最短路,枚举交点 x x x,分配价格,记录下最小值即为答案。
#include
using namespace std;
const int INF=1e9;
vector >adj;
void bfs(vector&d,int s){
d[s]=0;
queueque;
que.push(s);
while(!que.empty()){
int u=que.front();que.pop();
for(int i=0;ip(m+1);
for(int i=1;i<=m;i++) scanf("%d",&p[i]);
sort(p.begin()+1,p.end());
vectorpref(m+1,0);
for(int i=1;i<=m;i++){
pref[i]=pref[i-1]+p[i];
}
// for(int i=1;i<=m;i++) printf("%d\n",p[i]);
adj=vector >(n+1);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d %d",&x,&y);
adj[x].push_back(y);adj[y].push_back(x);
}
vectorda(n+1,INF),db(n+1,INF),dc(n+1,INF);
bfs(da,a),bfs(db,b),bfs(dc,c);
long long ans=1e18;
for(int x=1;x<=n;x++){
if(da[x]+db[x]+dc[x]>m) continue;
ans=min(ans,pref[db[x]]+pref[da[x]+db[x]+dc[x]]);
}
printf("%lld\n",ans);
}
return 0;
}
待补
第一次写题解,欢迎批评指正