【解题报告】CF练一下题 | 难度CF2500左右

  • Ciel and Gondolas | CF321E
    • 题意
    • 思路 | dp | 决策单调性 | 二维前缀和
    • 代码
  • Least Cost Bracket Sequence | CF3D
    • 题意
    • 思路 | 贪心
    • 代码
  • Buy Low Sell High | CF865D
    • 题意
    • 思路 | 贪心 | 可反悔贪心
    • 代码
  • Nearest Leaf | CF1110F
    • 题意
    • 思路 | 离线 | 线段树 | 换根
  • Inversion SwapSort | CF1375E
    • 题意
    • 思路 | 构造 | 排序
    • 代码
  • Arpa’s overnight party and Mehrdad’s silent entering | CF741C
    • 题意
    • 思路 | 构造 | 二分图
  • Kuroni and the Punishment | CF1305F
    • 题意
    • 思路 | 数学 | 随机化
    • 代码
  • Make It One | CF1043F
    • 题意
    • 思路 | 数学 | 容斥
    • 代码
  • Emotional Fishermen | CF1437F
    • 题意
    • 思路 | dp | 组合数学
    • 代码
  • 【解题报告】CF练一下题 | 难度CF2500

Ciel and Gondolas | CF321E

题意

  • Ciel and Gondolas | CF321E
    有 nnn 个人,你要把他分成连续的 kkk 段
    假设某段为 [L,R][L,R][L,R],那么花费为
    w[a,b]=∑L≤i<j≤Ru(i,j)w[a,b]=\sum_{L\le i<j\le R} u(i,j) w[a,b]=L≤i<j≤R∑​u(i,j)
    总的花费为 kkk 段的花费和,问总的花费的最小值是多少
  • 1≤n≤40001\le n\le 40001≤n≤4000
    1≤k≤min⁡(n,800)1\le k\le \min(n,800)1≤k≤min(n,800)
    0≤u(i,j)≤90\le u(i,j)\le 90≤u(i,j)≤9,满足 u(i,j)=u(j,i),u(i,i)=0u(i,j)=u(j,i),u(i,i)=0u(i,j)=u(j,i),u(i,i)=0

思路 | dp | 决策单调性 | 二维前缀和

  • 首先写出暴力的转移式子,设 dp[i][j]dp[i][j]dp[i][j] 表示考虑完前 iii 个人,分完 kkk 段的最小花费。转移如下:
    dp[i][j]=min⁡0≤s<i{dp[s][j−1]+w[s+1,i]}dp[i][j]=\min_{0\le s<i}\{ dp[s][j-1] + w[s+1,i] \} dp[i][j]=0≤s<imin​{dp[s][j−1]+w[s+1,i]}
    暴力转移,状态 nknknk 转移 n2n^2n2 总复杂度 O(n3k)O(n^3k)O(n3k),需要优化
  • 首先想想 w[a,b]w[a,b]w[a,b] 能否快速获得
    容易想到,w[a,b]w[a,b]w[a,b] 就是 12×∑i=ab∑j=abu(i,j)\frac{1}{2}\times \sum_{i=a}^b \sum_{j=a}^b u(i,j)21​×∑i=ab​∑j=ab​u(i,j),即这个矩阵的这个子矩阵的和
    可以使用二维前缀和,这样就可以 O(1)O(1)O(1) 获得 w[a,b]w[a,b]w[a,b] 了,总复杂度 O(n2k)O(n^2k)O(n2k),还需要优化
  • 引入一个概念,叫做四边形不等式

如果对于任意的 a1≤a2<b1≤b2a1≤a2<b1≤b2a1≤a2<b1≤b2
有 m[a1,b1]+m[a2,b2]≤m[a1,b2]+m[a2,b1]m[a1,b1]+m[a2,b2]≤m[a1,b2]+m[a2,b1]m[a1,b1]+m[a2,b2]≤m[a1,b2]+m[a2,b1]
那么 m[i,j]m[i,j]m[i,j] 满足四边形不等式。

  • 我们稍微画一下图,就可以发现 w[a,b]w[a,b]w[a,b] 是满足这个四边形不等式的
  • 再引入一个概念,叫做决策单调性
    若 f(i)f(i)f(i) 的转移决策点为 g(i)g(i)g(i), f(j)f(j)f(j) 的转移决策点为 g(j)g(j)g(j) 且 j<ij<ij<i,则 g(i)≥g(j)g(i)\ge g(j)g(i)≥g(j),即决策点单调不降
    若 dp[i]=min⁡{dp[j]+w[j+1,i]}dp[i]=\min\{ dp[j]+w[j+1,i]\}dp[i]=min{dp[j]+w[j+1,i]} 且 w[a,b]w[a,b]w[a,b] 满足四边形不等式,则 dp[i]dp[i]dp[i] 满足决策单调性
  • 但是考虑到新的点 iii ,我们只知道决策点的下界即 g(i−1)g(i-1)g(i−1) ,不知道决策点的上界,所以我们可以采用分治
    我们对于区间 [L,R][L,R][L,R],我们的决策区间为 [qL,qR][qL,qR][qL,qR],获取中点 mid=(L+R)/2mid=(L+R)/2mid=(L+R)/2,暴力跑出 midmidmid 的转移决策点 g(mid)g(mid)g(mid)
    对于区间 [L,mid−1][L,mid-1][L,mid−1],决策区间变为 [qL,g(mid)][qL,g(mid)][qL,g(mid)] 对于区间 [mid+1,R][mid+1,R][mid+1,R],决策区间变为 [g(mid),qR][g(mid),qR][g(mid),qR]
    于是我们总的复杂度变为 O(nklog⁡n)O(nk\log n)O(nklogn)

代码

#include<bits/stdc++.h>
using namespace std;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}
typedef long long ll;
const int MAX = 4e3+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;namespace Fast_IO{ ///orz laofuconst int MAXL((1 << 18) + 1);int iof, iotp;char ioif[MAXL], *ioiS, *ioiT, ioof[MAXL],*iooS=ioof,*iooT=ioof+MAXL-1,ioc,iost[55];char Getchar(){if (ioiS == ioiT){ioiS=ioif;ioiT=ioiS+fread(ioif,1,MAXL,stdin);return (ioiS == ioiT ? EOF : *ioiS++);}else return (*ioiS++);}void Write(){fwrite(ioof,1,iooS-ioof,stdout);iooS=ioof;}void Putchar(char x){*iooS++ = x;if (iooS == iooT)Write();}inline int read(){int x=0;for(iof=1,ioc=Getchar();(ioc<'0'||ioc>'9')&&ioc!=EOF;)iof=ioc=='-'?-1:1,ioc=Getchar();if(ioc==EOF)exit(0);for(x=0;ioc<='9'&&ioc>='0';ioc=Getchar())x=(x<<3)+(x<<1)+(ioc^48);return x*iof;}inline long long read_ll(){long long x=0;for(iof=1,ioc=Getchar();(ioc<'0'||ioc>'9')&&ioc!=EOF;)iof=ioc=='-'?-1:1,ioc=Getchar();if(ioc==EOF)exit(0);for(x=0;ioc<='9'&&ioc>='0';ioc=Getchar())x=(x<<3)+(x<<1)+(ioc^48);return x*iof;}/**#define lll __int128inline lll read_lll(){lll x=0;for(iof=1,ioc=Getchar();(ioc<'0'||ioc>'9')&&ioc!=EOF;)iof=ioc=='-'?-1:1,ioc=Getchar();if(ioc==EOF)exit(0);for(x=0;ioc<='9'&&ioc>='0';ioc=Getchar())x=(x<<3)+(x<<1)+(ioc^48);return x*iof;}*/template <class Int>void Print(Int x, char ch = '\0'){if(!x)Putchar('0');if(x<0)Putchar('-'),x=-x;while(x)iost[++iotp]=x%10+'0',x/=10;while(iotp)Putchar(iost[iotp--]);if (ch)Putchar(ch);}void Getstr(char *s, int &l){for(ioc=Getchar();ioc==' '||ioc=='\n'||ioc=='\t';)ioc=Getchar();if(ioc==EOF)exit(0);for(l=0;!(ioc==' '||ioc=='\n'||ioc=='\t'||ioc==EOF);ioc=Getchar())s[l++]=ioc;s[l] = 0;}void Putstr(const char *s){for(int i=0,n=strlen(s);i<n;++i)Putchar(s[i]);}
} // namespace Fast_IO
using namespace Fast_IO;int dp[MAX][805];
int pre[MAX][MAX];int now_K;int s(int L,int R){return pre[R][R] - pre[L-1][R] - pre[R][L-1] + pre[L-1][L-1];
}void solve(int L,int R,int qL,int qR){if(L > R)return;int mid = L + R >> 1;int ID  = qL;int tmp = INF;for(int i = qL;i <= qR && i <= mid;++i){int val = s(i,mid) + dp[i-1][now_K-1];if(val < tmp){tmp = val;ID = i;}}dp[mid][now_K] = tmp;solve(L,mid-1,qL,ID);solve(mid+1,R,ID,qR);
}int main()
{int n,k;n = read();k = read();for(int i = 0;i <= n;++i)for(int j = 0;j <= k;++j)dp[i][j] = INF;dp[0][0] = 0;for(int i = 1;i <= n;++i)for(int j = 1;j <= n;++j){int x = read();pre[i][j] = x + pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1];}for(int i = 1;i <= k;++i){now_K = i;solve(1,n,1,n);}int ans = dp[n][k] / 2;cout << ans;return 0;
}
/***/

Least Cost Bracket Sequence | CF3D

题意

  • Least Cost Bracket Sequence | CF3D
    给定一个长度为 nnn 的字符串,只包含 '(',')','?' 三种
    第 iii 个问号改成左括号的花费为 aia_iai​,改成右括号的花费为 bib_ibi​
    问你把所有的问号都改成括号后,整个字符串满足一个合法的括号序列,求最小花费值
    若无法完成,输出 -1
  • n≤5×104n\le 5\times 10^4n≤5×104
    1≤ai,bi≤1061\le a_i,b_i\le 10^61≤ai​,bi​≤106

思路 | 贪心

  • 难度 260026002600 的贪心…
    我们记一个 preprepre 表示前缀和,遇到左括号加一,遇到右括号减一。
    合法的括号序列即要求每个位置的 preprepre 都非负,且最后位置的 pre=0pre=0pre=0。
  • 我们假设每个问号位置都为右括号
    如果 pre<0pre<0pre<0 了,那么我们需要让之前某个问号变为的右括号改成左括号。
    即,我们每次需要额外代价最小的那个右括号改为左括号。
    用一个优先队列维护即可。
    思维难度都在假设。

代码

  • 时间复杂度:O(nlog⁡n)O(n\log n)O(nlogn)
#include<bits/stdc++.h>
using namespace std;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}
typedef long long ll;
const int MAX = 5e4+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;char ss[MAX];
int aa[MAX],bb[MAX];struct node{int val,pos;bool operator < (const node &ND)const{return val > ND.val;}
};priority_queue<node>Q;
char ans[MAX];
int main()
{scanf("%s%*c",ss+1);int n = strlen(ss+1);int cnt = 0;for(int i = 1;i <= n;++i){if(ss[i] == '?')cnt++;ans[i] = ss[i];}for(int i = 1;i <= cnt;++i){scanf("%d%d",&aa[i],&bb[i]);}int pre = 0;bool can = true;ll fen = 0;int ccnt = 0;for(int i = 1;i <= n;++i){if(ss[i] == '(')pre++;else if(ss[i] == ')'){pre--;}else{ccnt++;ans[i] = ')';fen += bb[ccnt];Q.push((node){aa[ccnt] - bb[ccnt],i});pre--;}
//        show(i,pre);if(pre < 0){if(Q.empty())can = false;else{fen += Q.top().val;ans[Q.top().pos] = '(';Q.pop();pre+=2;}}}if(pre > 0)can = false;if(!can)puts("-1");else{printf("%lld\n",fen);printf("%s",ans+1);}return 0;
}
/***/

Buy Low Sell High | CF865D

题意

  • Buy Low Sell High | CF865D
    给定长度为 nnn 的数组 pnp_npn​
    位置 iii 可以变成左括号,花费 pip_ipi​
    位置 iii 可以变成右括号,收益 pip_ipi​
    当然这个位置你也可以不管它。
    求最后是一个合法的括号序列,求收益最大是多少。
  • 2≤n≤3⋅1052\le n\le 3\cdot 10^52≤n≤3⋅105
    1≤pi≤1061\le p_i\le 10^61≤pi​≤106

思路 | 贪心 | 可反悔贪心

  • 这题是上一题的升级版诶… (我把题意转换过来了,题意是等价的)
    我们用上一题的思路貌似不能通过,因为有时候留着一些位置可以让总的收益更高
  • 我们还是想一下怎么贪心。
    首先假设我们这个位置放右括号,若某个左边的空位置改成左括号让总收益更大,那我们就这么干。若左边有多个位置,我们选择左边花费最小的,这是一个贪心。
  • 假设我们使用之前的位置 iii,匹配当前的位置 jjj,于是收益为 p[j]−p[i]p[j]-p[i]p[j]−p[i],但是有可能未来有一个位置 kkk,满足 p[k]−p[i]p[k]-p[i]p[k]−p[i] 或者 p[k]−p[j]p[k]-p[j]p[k]−p[j] 的收益更高,那么怎么办呢?
  • 首先由于我们匹配了 i,ji,ji,j,当然应该满足 p[j]>p[i]p[j]>p[i]p[j]>p[i]
    若未来的 kkk 匹配更优,那应该也满足 p[k]>p[i]p[k]>p[i]p[k]>p[i]
    即我们有 i<j<k,p[i]<p[j]<p[k]i<j<k,p[i]<p[j]<p[k]i<j<k,p[i]<p[j]<p[k]
  • 目前 kkk 改成和 iii 匹配的话,收益减去 p[j]−p[i]p[j]-p[i]p[j]−p[i] 再加上 p[k]−p[i]p[k]-p[i]p[k]−p[i] 即收益增加 p[k]−p[j]p[k]-p[j]p[k]−p[j],即 可反悔贪心
    目前 kkk 改成和 jjj 匹配的话,是不优的,所以我们不会这样决策。
  • 当前位置也可以空着,留着之后来匹配。
    注意:由于是可反悔贪心,我们这两种情况都要 push 进优先队列中去,而不是 kkk 和 iii 匹配更优的话我们就一定让 kkk 匹配掉了,因为有可能更后面的位置和 iii 匹配了把 kkk 留下了。但暂时收益的话仍然是加进去的。

代码

  • 时间复杂度:O(nlog⁡n)O(n\log n)O(nlogn)
#include<bits/stdc++.h>
using namespace std;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}
typedef long long ll;
const int MAX = 5e4+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;struct node{int val;bool operator <(const node &ND)const{return val < ND.val;}
};priority_queue<node>Q;int main()
{int n;scanf("%d",&n);ll now = 0;for(int i = 1;i <= n;++i){int c;scanf("%d",&c);if(i == 1){Q.push((node){-c});}else{int v = Q.top().val;
//            show(i,c,v);if(c + v > 0){now += c + v;Q.pop();Q.push((node){-c});     // 可反悔贪心}Q.push((node){-c});            // 空着}
//        show(i,now);}cout << now;return 0;
}
/***/

Nearest Leaf | CF1110F

题意

  • Nearest Leaf | CF1110F
    nnn 个点的一棵树,知道每个点的 dfndfndfn 序,树有边权 wiw_iwi​
    树的叶子就是度为 111 的点,且这个树的根度不为 111
    两个点的距离为这两个点的简单路径的边权和
    qqq 个询问,每个询问给定 v,L,Rv,L,Rv,L,R
    求 vvv 号点距离 xxx 的最近距离,其中 dfn(x)∈[L,R]dfn(x) \in[L,R]dfn(x)∈[L,R] 且 xxx 是一个叶子节点
  • 3≤n≤5×1053\le n\le 5\times 10^53≤n≤5×105
    1≤q≤5×1051\le q\le 5\times 10^51≤q≤5×105
    1≤wi≤1091\le w_i\le 10^91≤wi​≤109

思路 | 离线 | 线段树 | 换根

  • 这题可以离线。想到换根这题就可以直接过了。
    首先我们计算根到每个叶子的距离,放在线段树里。
    注意到,dfndfndfn 的性质,即可以使用线段树进行维护或者查询。
  • 我们把根换到某个子树,那么某一段的距离减少 www,某一段的距离增加 www,即线段树加操作。
    我们把根换到 vvv,查询 [L,R][L,R][L,R] 的叶子距离当前的最近距离,即线段树区间求最值。
    于是代码略。
  • 注:给定 dfndfndfn 的询问,直接考虑线段树。在线考虑不了,考虑离线,即换根。

Inversion SwapSort | CF1375E

题意

  • Inversion SwapSort | CF1375E
    给定长度为 nnn 的数组 ana_nan​
    它有 mmm 个逆序对 (i,j)(i,j)(i,j),其中满足 1≤i<j≤n,a[i]>a[j]1\le i<j\le n,a[i]>a[j]1≤i<j≤n,a[i]>a[j]
    你需要给出这 mmm 个逆序对的一个排列 PPP,第 iii 个逆序对为 (Li,Ri)(L_i,R_i)(Li​,Ri​)
    接下来进行 mmm 次操作,第 iii 次操作交换 a[Li],a[Ri]a[L_i],a[R_i]a[Li​],a[Ri​]
    操作完成后,满足 ana_nan​ 非递减。
  • 1≤n≤1031\le n\le 10^31≤n≤103

思路 | 构造 | 排序

  • 逆序对我们可以直接 O(n2)O(n^2)O(n2) 求出,关键是怎么排列这个顺序
    对于目前的数组 a[1]∼a[n]a[1]\sim a[n]a[1]∼a[n] ,我们知道里面最大的数字一定要交换到位置 nnn 去,然后让问题规模变成 b[1]∼b[n−1]b[1] \sim b[n-1]b[1]∼b[n−1]。
    若 a[n]a[n]a[n] 本来就是最大的数了,那么目前也没有 (?,n)(?,n)(?,n) 的逆序对了,直接考虑子问题。
    否则,肯定有一些逆序对 (?,n)(?,n)(?,n),我们目前只考虑这些逆序对。
  • 在交换完这些逆序对的位置后,我们希望让 b[1]∼b[n−1]b[1]\sim b[n-1]b[1]∼b[n−1] 仍然保持原先的一些性质,这样才是一个子问题。什么性质呢?即:
    1≤i<j<n若a[i]≥a[j],则b[i]≥b[j]若a[i]≤a[j],则b[i]≤b[j]1\le i<j<n\\ 若 a[i] \ge a[j] ,则 b[i]\ge b[j]\\ 若 a[i] \le a[j] ,则 b[i]\le b[j] 1≤i<j<n若a[i]≥a[j],则b[i]≥b[j]若a[i]≤a[j],则b[i]≤b[j]
    即让这些数字的相对大小不变。因为最后是一个排序,排序只用考虑数字之间的相对大小,不用考虑绝对大小。
  • 首先,目前 (x,n)(x,n)(x,n) 的逆序对肯定满足 a[x]>a[n]a[x] > a[n]a[x]>a[n] ,即大于 a[n]a[n]a[n] 的所有位置都有一个逆序对,小于等于 a[n]a[n]a[n] 的位置都没有逆序对。
    注意到,小于 a[n]a[n]a[n] 的值目前我们不需要变换,也可以满足他们之前的相对大小不改变。
    大于等于 a[n]a[n]a[n] 的值,我们需要依次让他们低一位,他们的相对大小才能不改变。
    举例: 6,2,1,5,7,3,4 我们需要变成 x,2,1,x,x,3,7(最大值放最后面,最小值不动),然后再调整成 5,2,1,4,6,3,7 ,即其他每个位置都顺次低一位,这样他们之间的相对顺序不变。
  • 若有多个相同值的位置,我们记录他们的下标,若值相同,下标小的值我们记他们更小。
    这样的话,每次我们选择前面比他大的最大的位置 ppp,交换 aa[p],aa[n]aa[p],aa[n]aa[p],aa[n] 即可。

代码

  • 时间复杂度:O(n2log⁡n)O(n^2\log n)O(n2logn)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}const int MAX = 1e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}int aa[MAX];
priority_queue<pair<int,int> >Q;
int main()
{int n;cin >>n;for(int i = 1;i <= n;++i){cin >> aa[i];}int cnt = 0;for(int i = 1;i <= n;++i){for(int j = i+1;j <= n;++j){if(aa[i] > aa[j]){cnt++;}}}cout << cnt << '\n';for(int i = n;i >= 2;--i){for(int j = 1;j < i;++j){if(aa[j] > aa[i]){Q.push(make_pair(-aa[j],-j) );}}while(!Q.empty()){int p = -Q.top().second;Q.pop();cout << p << " " << i << '\n';}}return 0;
}
/**
6 1 9 2 3 4 57
6 1 5 2 3 7 46
2 5 3 1 7 65 2 1 5 3 4
*/

Arpa’s overnight party and Mehrdad’s silent entering | CF741C

题意

  • Arpa’s overnight party and Mehrdad’s silent entering | CF741C
    有 2n2n2n 个位置排成一个环,这些位置分为 nnn 个对
    你需要让每个位置赋值 111 或者 222,满足:
  • 其中第 iii 个对为 (ai,bi)(a_i, b_i)(ai​,bi​),必须满足这两个位置的值不同
  • 环上,连续三个位置的值不能都相同
    若无解,输出 -1,否则输出一个构造解

思路 | 构造 | 二分图

  • 两种颜色染色,优先考虑二分图!
    容易想到,ai,bia_i,b_iai​,bi​ 之间连一条边,表示他们的颜色不能相同。
  • 考虑连续三个位置的值不能都相同怎么处理
    想到一种构造方案,我们连边所有的 2k,2k−12k,2k-12k,2k−1
    为什么这样一定是可行的呢?
  • 首先,若无解,则二分图有奇环,否则是一定有解的。
    首先我们先连边 2k,2k−12k,2k-12k,2k−1,然后再连边所有的 ai,bia_i,b_iai​,bi​
    容易想到,每个点最多连出去两条边,即最终每个连通分量要么是一个链,要么是一个环
    链无所谓,我们考虑环。环的奇偶性即为环上点的个数的奇偶性。
    一开始的连边2k,2k−12k,2k-12k,2k−1,满足每个连通分量的点都为 222。后续的 ai,bia_i,b_iai​,bi​ 的连边,即合并了某两个连通分量,且这两个连通分量的点为偶数个,那么新的连通分量的点也是偶数个。于是一定有解。
    注意我们讨论的前提是点的度最大为 222
    代码略

Kuroni and the Punishment | CF1305F

题意

  • Kuroni and the Punishment | CF1305F
    给定长度为 nnn 的数组 ana_nan​
    一次操作,可以让某个数字加一或者减一,注意操作后数字必须非负
    问你最少的操作次数,让数组的 gcd>1gcd>1gcd>1
  • 2≤n≤2⋅1052\le n\le 2\cdot 10^52≤n≤2⋅105
    1≤ai≤10121\le a_i\le 10^{12}1≤ai​≤1012

思路 | 数学 | 随机化

  • 假设 gcd=2gcd=2gcd=2,那么答案最大为 nnn,即这个数组中奇数的个数。那么我们需要求出 <n<n<n 的答案。
  • 考虑 gcd=kgcd=kgcd=k,那么我们很容易求出答案:
    ans=∑i=1n{min(ai%k,k−ai%k)ai≥kk−aiai<kans=\sum_{i=1}^n \begin{cases} min(a_i\%k,k-a_i\%k)& a_i\ge k\\ k-a_i&a_i<k \end{cases} ans=i=1∑n​{min(ai​%k,k−ai​%k)k−ai​​ai​≥kai​<k​
  • 注意到,首先枚举质因子肯定是优于枚举所有的因子的。
    aia_iai​ 的最多质因子为 121212 个,总共的质因子最多为 12n12n12n,一次 checkcheckcheck 是 O(n)O(n)O(n) 的,看起来 O(12n2)O(12n^2)O(12n2),但是大多数操作算到一半我们的 ansansans 就很大了,因此这里其实是算的很快的
    关键是我们算出这所有的质因子,不管是直接根号算还是使用 Pollard_RhoPollard\_RhoPollard_Rho 都是 TL 飞起的。\
  • 质因子有很多,但是我们感觉随便取一些就可以得到最小的答案了,这启发我们使用 随机化
    若答案 <n<n<n,那么至少有一半的位置最多被操作一次。
    那么我们枚举至多被操作一次的数字 aia_iai​,就有二分之一的概率选中最优的那种操作的方案。
    于是我们可以枚举 mmm 次,算对的概率即为 1−12m1-\frac{1}{2^m}1−2m1​
    我们可以 random_shuffle,然后选择前 202020 个数字,即随机选择了 202020 个数字
    然后枚举他们 ai+1,ai,ai−1a_i+1,a_i,a_i-1ai​+1,ai​,ai​−1 的所有质因子作为答案去 checkcheckcheck 即可
  • 注:我选择 101010 个后 wa200 了,运气感人。

代码

  • 时间复杂度:O(60×(1012+12n))O(60\times (\sqrt{10^{12}}+12n))O(60×(1012​+12n))
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}const int MAX = 2e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}ll aa[MAX];
map<ll,bool>M;void check(ll x){for(ll i = 2;i * i <= x;++i){if(x % i == 0){M[i] = 1;while(x % i == 0){x /= i;}}}if(x > 1)M[x] = 1;
}int main()
{int n;scanf("%d",&n);for(int i = 1;i <= n;++i){scanf("%lld",&aa[i]);}random_shuffle(aa+1,aa+1+n);random_shuffle(aa+1,aa+1+n);random_shuffle(aa+1,aa+1+n);for(int i = 1;i <= min(20,n);++i){ll now = aa[i];if(now > 1){check(now);}now = aa[i]+1;if(now > 1){check(now);}now = aa[i]-1;if(now > 1){check(now);}}ll ans = n;for(auto [a,b] : M){ll tmp = 0;for(int i = 1;i <= n;++i){if(aa[i] <= a)tmp += (a - aa[i]);elsetmp += min(aa[i] % a,a - aa[i] % a);if(tmp > ans)break;}ans = min(ans,tmp);}printf("%lld",ans);return 0;
}
/***/

Make It One | CF1043F

题意

  • Make It One | CF1043F
    给定 ana_nan​,求最小的子集大小,满足这个子集的元素的 gcd=1gcd=1gcd=1
    若无解,输出 −1-1−1
  • 1≤n≤3×1051\le n\le 3\times 10^51≤n≤3×105
    1≤ai≤1051\le a_i\le 10^51≤ai​≤105

思路 | 数学 | 容斥

  • 首先我们 -1 判断很简单,若 ana_nan​ 的 gcd>1gcd>1gcd>1 肯定是无解的
    考虑到,aia_iai​ 最多有 777 个不同的质因子,即答案 ≤7\le 7≤7
  • 假设目前的答案为 kkk,即我们能否找到 kkk 个元素,让他们的 gcd=1gcd=1gcd=1
    非常套路的,我们令 f(i)f(i)f(i) 表示 gcd=igcd=igcd=i 的方案数,令 F(i)F(i)F(i) 表示 gcdgcdgcd 为 iii 的倍数的方案数
    f(i)=∑i∣jμ(ji)F(j)f(i)=\sum_{i|j}\mu(\frac{j}{i})F(j) f(i)=i∣j∑​μ(ij​)F(j)
  • 即我们怎么快速算 F(i)F(i)F(i),答案就是 f(1)f(1)f(1)
    我们记 barr[i]barr[i]barr[i] 表示有多少个数为 iii 及其倍数,这里可以 O(Rlog⁡R)O(R\log R)O(RlogR) 预处理得到
    即答案为 Cbarr[i]kC_{barr[i]}^kCbarr[i]k​,因为我们要选 kkk 个数。
    若 f(1)>0f(1)>0f(1)>0 表示可行。

代码

  • 时间复杂度:O(nlog⁡n+Rlog⁡R)O(n\log n + R\log R)O(nlogn+RlogR)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}const int MAX = 3e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}ll fac[MAX],ivfac[MAX];void init(int n){fac[0] = 1;for(int i = 1;i <= n;++i)fac[i] = fac[i-1] * i % MOD;ivfac[n] = inv(fac[n]);for(int i = n - 1;~i;--i)ivfac[i] = ivfac[i+1] * (i+1) % MOD;
}ll C(int n,int m){if(n < 0 || m > n)return 0;return fac[n] * ivfac[m] % MOD * ivfac[n - m] % MOD;
}bool vis[MAX];
int prime[MAX];
int mu[MAX];
int cnt;
void shai(int n){mu[1] = 1;for(int i = 2;i <= n;++i){if(!vis[i]){prime[++cnt] = i;mu[i] = -1;}for(int j = 1;j <= cnt && i * prime[j] <= n;++j){vis[i*prime[j]] = 1;if(i % prime[j])mu[i*prime[j]] = -mu[i];else {mu[i*prime[j]] = 0;break;}}}
}int barr[MAX];ll F(int x,int shu){return C(barr[x],shu);
}int main()
{shai(300000);init(300000);int n;scanf("%d",&n);int gcd = 0;for(int i = 1;i <= n;++i){int x;scanf("%d",&x);barr[x] = 1;gcd = __gcd(gcd,x);}if(gcd > 1){puts("-1");return 0;}for(int i = 1;i <= 300000;++i)for(int j = 2;i * j <= 300000;++j){barr[i] += barr[i*j];}for(int i = 1;i <= 7;++i){ll now = 0;for(int j = 1;j <= 300000;++j){now = (now + mu[j] * F(j,i) % MOD + MOD) % MOD;}
//        show(i,now);if(now > 0){printf("%d",i);break;}}return 0;
}

Emotional Fishermen | CF1437F

题意

  • Emotional Fishermen | CF1437F
    给定 ana_nan​,你需要问你有多少种它的排列后的数组 bnb_nbn​ ,满足:
    pre[i]=max⁡j=1i{b[i]},pre[0]=0b[i]≥2⋅pre[i−1]或者2⋅b[i]≤pre[i−1]成立pre[i] = \max_{j=1}^{i}\{ b[i]\},pre[0]=0\\ b[i] \ge 2\cdot pre[i-1] 或者 2\cdot b[i] \le pre[i-1] 成立 pre[i]=j=1maxi​{b[i]},pre[0]=0b[i]≥2⋅pre[i−1]或者2⋅b[i]≤pre[i−1]成立
  • 方案数取模 998244353998244353998244353
    2≤n≤5×1032\le n\le 5\times10^32≤n≤5×103
    1≤ai≤1091\le a_i\le 10^91≤ai​≤109

思路 | dp | 组合数学

  • 首先可对 ana_nan​ 进行升序排序
    考虑计算 dp[i]dp[i]dp[i] 表示处理完前 iii 个数,且放进去了 a[i]a[i]a[i]的方案数。 显然目前的 pre[i]=a[i]pre[i]=a[i]pre[i]=a[i]
    我们考虑每次从前面已处理到一半的数组中,新放进去一些数字。我们只要考虑新放进去数字的个数,他们的相对位置。
    想一下,目前有一些数字可能是放不进去的,放不进去的数字的值范围为 (a[i]/2,a[i])(a[i]/2,a[i])(a[i]/2,a[i]),容易发现他们的下边是连续的。且目前 a[i]a[i]a[i] 一定是放在最后面的。
    我们记录 lim[i]lim[i]lim[i] 表示最大的满足 jjj,满足 2⋅a[j]≤a[i]2\cdot a[j]\le a[i]2⋅a[j]≤a[i]
    那么,目前我们对于 dp[i]dp[i]dp[i],我们放进去的数字的个数为 lim⁡[i]+1\lim[i]+1lim[i]+1,其他的数字目前还放不进去。
  • 我们考虑从 dp[j]dp[j]dp[j] 转移到 dp[i]dp[i]dp[i],当然 j<ij<ij<i 且 2⋅a[j]≤a[i]2\cdot a[j]\le a[i]2⋅a[j]≤a[i]
    对于 dp[j]dp[j]dp[j],能放进去的数的下标为 [1,lim[j]][1,lim[j]][1,lim[j]];对于 dp[i]dp[i]dp[i],能放进去的数的下标为 [1,lim[i]][1,lim[i]][1,lim[i]]
    即我们需要新/多放进去 lim[i]−lim[j]−1lim[i]-lim[j]-1lim[i]−lim[j]−1 个数字。注意 a[j]a[j]a[j] 放在哪里我们不知道,也需要额外考虑进去。但是 a[i]a[i]a[i] 是放在最后的,不需要考虑。
    对于 dp[j]dp[j]dp[j],我们已经放入了 lim⁡[j]+1\lim[j]+1lim[j]+1 个数字,一共有 nnn 个数字,还剩下 n−lim⁡[j]−1n-\lim[j]-1n−lim[j]−1 个位置,其中最后的位置是放置 a[i]a[i]a[i] 的,即我们还有 n−lim⁡[j]−2n-\lim[j]-2n−lim[j]−2 个位置。
    仍然注意,这里的位置是相对位置不是绝对位置。
    那么这些数字放进这些位置中,任意都可以摆放,方案数自然为 An−lim⁡[j]−2lim[i]−lim[j]−1A_{n-\lim[j]-2}^{lim[i]-lim[j]-1}An−lim[j]−2lim[i]−lim[j]−1​
    综上,我们的转移为:
    dp[i]=∑j=0lim⁡[i]dp[j]×An−lim⁡[j]−2lim[i]−lim[j]−1dp[i]=\sum_{j=0}^{\lim[i]} dp[j]\times A_{n-\lim[j]-2}^{lim[i]-lim[j]-1} dp[i]=j=0∑lim[i]​dp[j]×An−lim[j]−2lim[i]−lim[j]−1​
  • 最后注意下无解,即 2a[n−1]>a[n]2a[n-1]>a[n]2a[n−1]>a[n] 是非法的。
    注意,我们的时间复杂度为 O(n2)O(n^2)O(n2),但这题可以优化成 O(nlog⁡n)O(n\log n)O(nlogn),首先 lim[x]lim[x]lim[x] 使用二分进行求解,然后 dp[x]dp[x]dp[x] 可以稍微化简一下:
    dp[i]=∑j=0lim⁡[i]dp[j]×(n−lim[j]−2)!(n−lim[i]−1)!=1(n−lim[i]−1)!×Pre[lim[i]]dp[i]=\sum_{j=0}^{\lim[i]} dp[j]\times \frac{(n-lim[j]-2)!}{(n-lim[i]-1)!}=\frac{1}{(n-lim[i]-1)!} \times Pre[lim[i]] dp[i]=j=0∑lim[i]​dp[j]×(n−lim[i]−1)!(n−lim[j]−2)!​=(n−lim[i]−1)!1​×Pre[lim[i]]
    即可以快速求出。

代码

  • 时间复杂度:O(n2)O(n^2)O(n2)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}const int MAX = 5e3+50;
const int MOD = 998244353;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}int aa[MAX];
int lim[MAX];
ll f[MAX];ll fac[MAX],ivfac[MAX];void init(int n){fac[0] = 1;for(int i = 1;i <= n;++i)fac[i] = fac[i-1] * i % MOD;ivfac[n] = inv(fac[n]);for(int i = n - 1;~i;--i)ivfac[i] = ivfac[i+1] * (i+1) % MOD;
}ll A(int n,int m){if(n < 0 || m > n)return 0;return fac[n] * ivfac[n-m] % MOD;
}int main()
{init(5000);int n;cin >>n;for(int i = 1;i <= n;++i)cin >>aa[i];sort(aa+1,aa+1+n);if(2*aa[n-1] > aa[n]){puts("0");return 0;}for(int i = 1;i <= n;++i){for(int j = 1;j < i;++j){if(2*aa[j] <= aa[i]){lim[i] = j;}else{break;}}}f[0] = 1;lim[0] = -1;for(int i = 1;i <= n;++i){for(int j = 0;j <= lim[i];++j){f[i] = (f[i] + f[j] * A(n - lim[j] - 2,lim[i] - lim[j] - 1) % MOD) % MOD;}}cout << f[n];return 0;
}
/***/

【解题报告】CF练一下题 | 难度CF2500左右相关推荐

  1. 计算机解题报告,计算机题目33题(附带题解)精选.doc

    第1~10题为基础题,第11~20题为提高题,第21~33为综合题 每位同学分别从基础题.提高题.综合题中各选一题,按学号顺序循环选择,用word写报告.每班评出优秀的9名同学,上台做报告,每人期末成 ...

  2. [解题报告]【第16题】给定 n,打印一个直角边为 n 的等边直角三角形

    目录 零.写在前面 一.主要知识点 1.打印指定的形状的边界判定 2.信号量 二.课后习题 1115. 交替打印FooBar 写在最后 零.写在前面 这个系列不经常更新,今天这个题目因为英雄哥玩超纲课 ...

  3. 群赛 round#8 解题报告一 (swop,ranwen,easy)

    群赛 round#8 解题报告一 赛制: OI 难度: noip T1 交换!交换!(swop) [问题描述] ljm喜欢交换物品,他觉得这样可以与更多人分享好的事物. 有一天,lzx给了ljm n本 ...

  4. 人生第一场CTF的解题报告(部分)

    解题报告 濮元杰部分: 王者归来: 120 场景 小王入职了一段时间,最近有点无聊.Web安全项目不多,白天看着其他项目组的同事忙得热火朝天,小王有点坐不住了,这也许是新人都会有的想法,乐于助人.想到 ...

  5. 【解题报告】博弈专场 (CF 2000~2200)前五题

    [解题报告]博弈专场 (CF 2000+)前五题 A:Fox and Card Game | CF388C 题意 思路 代码 B:Berzerk | CF786A 题意 思路 代码 C:Ithea P ...

  6. 解题报告(十八)数论题目泛做(Codeforces 难度:2000 ~ 3000 + )

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一 ...

  7. 【解题报告系列】超高质量题单 + 题解(ACM / OI)超高质量题解

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我新写的超高质量的题解和代码,题目难度不 ...

  8. 攻防世界XCTF-WEB入门12题解题报告

    WEB入门题比较适合信息安全专业大一学生,难度低上手快,套路基本都一样 需要掌握: 基本的PHP.Python.JS语法 基本的代理BurpSuite使用 基本的HTTP请求交互过程 基本的安全知识( ...

  9. 【解题报告】CF DIV2 #ROUND 723 A~D

    [解题报告]CF DIV2 #ROUND 723 A~D 比赛链接 比赛评价: 发现这场十点就开了,然后就和ph巨佬一起玩了一场.我两分别再A和B罚时罚飞了,索性后面把C1,C2整出来了 排名2500 ...

  10. 【解题报告】随便练练二(CF 2300)

    [解题报告]随便练练二(CF 2300) A:Antimatter | CF383D 题意 思路 :DP 代码 B:Physical Education Lessons | CF915E 题意 思路一 ...

最新文章

  1. 2014 年美国程序员薪资调查
  2. 奖金+招聘绿色通道,这一届算法大赛关注下?
  3. 复习宝典之Maven项目管理
  4. 批处理相对路径51CTO自动领豆(Python)
  5. POJ - 3700 Missile Defence System.(dfs+最优性剪枝)
  6. 在 SAP Spartacus 里如何调用 hybris 里实现的自定义 API
  7. 职业学校计算机知识试卷答案,2016中等职业学校计算机等级考试题库(含答案)计算机基础题库...
  8. 电脑显示器闪屏_时尚超薄可升降:华硕新品家用护眼显示器MZ27AQL
  9. 通讯录小程序android,通讯录小程序,找回青春的回忆
  10. windows文件(.txt,.h,.cpp等等)中的中文在ubuntu下乱码的解决方法
  11. 24个坏行为让你身体越来越丑
  12. Hadoop2.8集群安装详细教程
  13. CentOS7下简单安装python3.7.0步骤
  14. 问题解决——OpenGL超级宝典 关于gltDrawTorus的错误解决
  15. L2TP/L2TP over IPSec
  16. 微信小程序引入 vant UI组件库
  17. Samba服务------SMB协议
  18. 拦截QT关闭窗口的CloseEvent()解析
  19. 《Single-Shot Object Detection with Enriched Semantics》论文笔记
  20. 用Python代码来下载任意指定网易云歌曲

热门文章

  1. Qt多线程之QtConcurrent
  2. 【图像隐藏】基于matlab像素预测和位平面压缩的加密图像可逆数据隐藏【含Matlab源码 2218期】
  3. Wireshark 64位中文版(抓包工具)
  4. 一个IT技术人如果转型做自由职业可以做哪些方向?
  5. java报错establishing_JDBC连接SQLServer时出现错误Error establishing socket.的解决。
  6. mysql的auto_increment详解
  7. unbuntu基本操作
  8. 春季必买明星款流行春装
  9. 安装QT时遇到:canot start “d:\qt\vcredist\vcredist_x64.exe/norestart/q“:process failed to start :请求的操作需要提升
  10. 用biobert标记基因和蛋白质