【解题报告】CF练一下题 | 难度CF2500左右
【解题报告】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]=min0≤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=abu(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(nklogn)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(nlogn)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(nlogn)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(n2logn)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−aiai≥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(RlogR)O(R\log R)O(RlogR) 预处理得到
即答案为 Cbarr[i]kC_{barr[i]}^kCbarr[i]k,因为我们要选 kkk 个数。
若 f(1)>0f(1)>0f(1)>0 表示可行。
代码
- 时间复杂度:O(nlogn+RlogR)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]=maxj=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(nlogn)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左右相关推荐
- 计算机解题报告,计算机题目33题(附带题解)精选.doc
第1~10题为基础题,第11~20题为提高题,第21~33为综合题 每位同学分别从基础题.提高题.综合题中各选一题,按学号顺序循环选择,用word写报告.每班评出优秀的9名同学,上台做报告,每人期末成 ...
- [解题报告]【第16题】给定 n,打印一个直角边为 n 的等边直角三角形
目录 零.写在前面 一.主要知识点 1.打印指定的形状的边界判定 2.信号量 二.课后习题 1115. 交替打印FooBar 写在最后 零.写在前面 这个系列不经常更新,今天这个题目因为英雄哥玩超纲课 ...
- 群赛 round#8 解题报告一 (swop,ranwen,easy)
群赛 round#8 解题报告一 赛制: OI 难度: noip T1 交换!交换!(swop) [问题描述] ljm喜欢交换物品,他觉得这样可以与更多人分享好的事物. 有一天,lzx给了ljm n本 ...
- 人生第一场CTF的解题报告(部分)
解题报告 濮元杰部分: 王者归来: 120 场景 小王入职了一段时间,最近有点无聊.Web安全项目不多,白天看着其他项目组的同事忙得热火朝天,小王有点坐不住了,这也许是新人都会有的想法,乐于助人.想到 ...
- 【解题报告】博弈专场 (CF 2000~2200)前五题
[解题报告]博弈专场 (CF 2000+)前五题 A:Fox and Card Game | CF388C 题意 思路 代码 B:Berzerk | CF786A 题意 思路 代码 C:Ithea P ...
- 解题报告(十八)数论题目泛做(Codeforces 难度:2000 ~ 3000 + )
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一 ...
- 【解题报告系列】超高质量题单 + 题解(ACM / OI)超高质量题解
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我新写的超高质量的题解和代码,题目难度不 ...
- 攻防世界XCTF-WEB入门12题解题报告
WEB入门题比较适合信息安全专业大一学生,难度低上手快,套路基本都一样 需要掌握: 基本的PHP.Python.JS语法 基本的代理BurpSuite使用 基本的HTTP请求交互过程 基本的安全知识( ...
- 【解题报告】CF DIV2 #ROUND 723 A~D
[解题报告]CF DIV2 #ROUND 723 A~D 比赛链接 比赛评价: 发现这场十点就开了,然后就和ph巨佬一起玩了一场.我两分别再A和B罚时罚飞了,索性后面把C1,C2整出来了 排名2500 ...
- 【解题报告】随便练练二(CF 2300)
[解题报告]随便练练二(CF 2300) A:Antimatter | CF383D 题意 思路 :DP 代码 B:Physical Education Lessons | CF915E 题意 思路一 ...
最新文章
- 2014 年美国程序员薪资调查
- 奖金+招聘绿色通道,这一届算法大赛关注下?
- 复习宝典之Maven项目管理
- 批处理相对路径51CTO自动领豆(Python)
- POJ - 3700 Missile Defence System.(dfs+最优性剪枝)
- 在 SAP Spartacus 里如何调用 hybris 里实现的自定义 API
- 职业学校计算机知识试卷答案,2016中等职业学校计算机等级考试题库(含答案)计算机基础题库...
- 电脑显示器闪屏_时尚超薄可升降:华硕新品家用护眼显示器MZ27AQL
- 通讯录小程序android,通讯录小程序,找回青春的回忆
- windows文件(.txt,.h,.cpp等等)中的中文在ubuntu下乱码的解决方法
- 24个坏行为让你身体越来越丑
- Hadoop2.8集群安装详细教程
- CentOS7下简单安装python3.7.0步骤
- 问题解决——OpenGL超级宝典 关于gltDrawTorus的错误解决
- L2TP/L2TP over IPSec
- 微信小程序引入 vant UI组件库
- Samba服务------SMB协议
- 拦截QT关闭窗口的CloseEvent()解析
- 《Single-Shot Object Detection with Enriched Semantics》论文笔记
- 用Python代码来下载任意指定网易云歌曲
热门文章
- Qt多线程之QtConcurrent
- 【图像隐藏】基于matlab像素预测和位平面压缩的加密图像可逆数据隐藏【含Matlab源码 2218期】
- Wireshark 64位中文版(抓包工具)
- 一个IT技术人如果转型做自由职业可以做哪些方向?
- java报错establishing_JDBC连接SQLServer时出现错误Error establishing socket.的解决。
- mysql的auto_increment详解
- unbuntu基本操作
- 春季必买明星款流行春装
- 安装QT时遇到:canot start “d:\qt\vcredist\vcredist_x64.exe/norestart/q“:process failed to start :请求的操作需要提升
- 用biobert标记基因和蛋白质