Educational Codeforces Round 84 (Rated for Div. 2)
打爆的一场…
A
不写longlongWA3
B
写起来很恶心…题目还读错了…
考虑没有配对的公主和王子即可。
C
虽然知道是思维题但是就是想不到…淦!
题目允许移动2mn次,所以可以先花费至多m+n-2次将所有点堆叠在[1, 1]上,然后再用mn - 1次遍历所有方格,这样操作后每个点在遍历方格时一定会经过所需要经过的那个点。
很多时候题目做不出来要再读几遍题目…把一些关键的信息牢记于心…昨晚的[L, R]就是因为读题不慎而做不出来…
D
emm不算很难的题目,但是场上跟了风,没看D,去尝试做了E,但E也没做出来。。
之前在学习polya定理的时候稍微接触了一番置换群的有关概念,有了置换群的前置知识后这道题其实就没有什么难点了。
考虑k给定的情况,就是用步长k在置换群上造圆。题目要求找出最小的k,使得存在这样一个圆,圆上所有元素的颜色相同。
赛后补题自己写的时候用了素因子去划分每个小圆,但是这样的算法其实很假。尽管想到了暴力找所有的因子(而不仅仅是素因子),但是感觉时间复杂度撑不住,就没有写下去。
然而标答就是枚举所有可能的因子,对每个因子
O
(
m
)
O(m)
O(m)的查询一次是否存在同色圆。事实上,这样做的时间复杂度并不很大: 找所有的因子是线性的,复杂度
O
(
m
)
O(m)
O(m);对于每个因子的查询是遍历一次小圆,可以认为复杂度接近
O
(
m
)
O(m)
O(m);而对于总和不超过2e5的一堆数字,如果某个数字本身不大,那么尽管它占用因子的效率更高,但是在
O
(
m
)
O(m)
O(m)查询时也由于其本身不大所以总和也不会太大,如果某个数字本身较大,那么虽然这个数字需要花费更多的时间,但是其余剩余的数字就会更小;而且在2e5内,某个数最多有100小几个因子,最小的这样的数是50400,有108个因子,哪怕有4个50400,也只会花费410050400 = 2e7的时间;再加上我们在判断的时候加上了i < ans
的最优性剪枝,所以这样暴力遍历是完全ok的。
代码
const int inf = 1 << 30;
const int maxn = 2e5 + 10;
int p[maxn], c[maxn];
int N, ans;
struct{
vector<int> vec;
int n;
void solve(int p){
int ok, OK = 0;
//划分成了p个小圆
for(int j = 0; j < p; j++){
ok = 1;
int k = j + p;
while(k < n){
if(c[vec[k]] != c[vec[k - p]]){
ok = 0;
break;
}
k += p;
}
OK |= ok;
}
if(OK) ans = min(ans, p);
}
void sove(){
n = (int)vec.size();
ans = min(ans, n);
for(int i = 1; i < n && i < ans; i++) if(n % i == 0)
solve(i);
}
}circle[maxn];
int vis[maxn];
int main(){
// Fast;
// getprime();
int t; scanf("%d", &t); while(t--){
ans = inf; N = 0;
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", p + i);
vis[i] = 0;
}
for(int i = 1; i <= n; i++) scanf("%d", c + i);
for(int i = 1; i <= n; i++) if(!vis[i]){
int now = i;
circle[N].vec.clear();
while(!vis[now]){
circle[N].vec.push_back(now);
vis[now] = 1;
now = p[now];
}
N++;
}
for(int i = 0; i < N; i++) circle[i].sove();
printf("%d\n", ans);
}
return 0;
}
E
组合数学学的太菜了…
直接用乘法原理考虑一个长度为d的块在整个序列中的某个位置时可能产生的所有种数;不需要考虑重复的情况是因为如果一个数字中出现了多个d块,每一个位置的d块我们都会进行一次以它为核心的计数,而这整个数字在每次计数中都只会贡献1
次,所以总共来看这若干个d块恰贡献了这些d块的个数次,也就不重不漏的完成了计数工作。
组合数学不能再咕了!🐦要赶紧开始学习了qwq