1、前后缀贪心,比如说观察前后缀的sum,去看以后怎么考虑最好。Problem - 1903C - Codeforces
2、双指针贪心法,考虑两端相消或者相互作用,还有就是考虑左右边界。 Problem - 1891C - Codeforces
Problem - 1907D - Codeforces
3、转换观察法,有些关系可以抽象成图,观察图的某些性质去总结规律。也可以抽象成一个集合,两个集合相等可以说明有解可构造。Problem - 1891C - Codeforces
4、打表找规律,一般没什么规律可循即可打表找规律,一般和数论有关的很喜欢考,acm也喜欢考,属于人类智慧题。Problem - 1916D - Codeforces
5、公式推导演算,常见的分为公式的等价变形、公式的化简(这个常考,一般需要先证明某些性质,可以直接抵消,一般如果原公式处理起来很复杂时就可以考虑)。Problem - 1889B - Codeforces
6、考虑奇偶数去简化问题或者分类问题,从其中的一些运算性质入手,因为奇数偶数的加减以及%运算(这个结论很重要)的结果的奇偶性是固定的,Problem - 1898C - Codeforces
7、根据性质构造模型,看看能不能分成几个块,几个不同的集合,再选择算法去解决。Problem - 1873G - Codeforces
8、考虑从小到大处理,或者是从大到小处理,有时候先处理小的对大的不会有影响,或者反过来,这样的处理顺序是最完美的。Problem - 1904D2 - Codeforces
9、边界贪心法,一般要在问题的最边界处考虑,有时候这样做结果是最优的,或者考虑边界上的影响,假如让影响最小,就使得影响<= 固定值 。 Problem - E - Codeforces and Problem - 1903C - Codeforces
题意: 给定长度为n的两个序列a, b和一个数字k,可以进行以下的操作: 选定一个set L:[x,y,z...], length(L) = k, a[x] = y, a[y] = z, ... a[L[end]] = L[1],a最初都是0,问最后能不能构造成b,b[i] >= 1 and b[i] <= n
题解: 对于数组b再根据操作的性质,我们先对其建模,将b[i] -> b[b[i]],这样子其实构成了一张有向图,现在来推导以下图上的一些性质,首先是图上有环,说明可以互相推导,此时分类讨论: 1、环长为k个节点,说明可以构造一个set推出这k个节点 2、环长大于k个节点,说明无论如何都会有两个点是无法确认的,所以在这个环的某个边界上必须要指向环长为k的环的某个点,这样子可以进行传递到长度为k的环,先”牺牲“长度为k环上的点。 3、和2同理。 算法设计: 因为环是强连通分量,所以我第一反应是用tarjan算法先处理好图,然后再用并查集和度数判断,如果同一个连通块中有长度为k的环且出度为0,那么这个连通块必然能推导出来。
代码:并查集 + tarjan, 代码很长,看solve()其实就可以了。
#include
#define ff first
#define ss second
#define pb push_back
using namespace std;
using PII = pair;
const int N = 2e5 + 10;
int n, k;
int a[N];
int h[N], e[N << 1], ne[N << 1], idx;
int dout[N];
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, cnt[N];
int p[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void targan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk[++ top] = u, in_stk[u] = 1;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(!dfn[j])
{
targan(j);
low[u] = min(low[u], low[j]);
}
else if(in_stk[j]) low[u] = min(low[u], low[j]);
}
if(dfn[u] == low[u])
{
++ scc_cnt;
int y;
do {
y = stk[top --];
in_stk[y] = 0;
id[y] = scc_cnt;
cnt[scc_cnt] ++;
}while(y != u);
}
}
int find(int x) {
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
void solve() {
idx = 0, timestamp = 0, top = 0, scc_cnt = 0;
cin >> n >> k;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
if(k == 1) {
for(int i = 1; i <= n; i ++ ) {
if(a[i] != i) {
cout << "No" << endl;
return;
}
}
cout << "Yes" << endl;
return;
}
for(int i = 0; i <= n; i ++ ) h[i] = -1, cnt[i] = 0, id[i] = 0, in_stk[i] = 0, dout[i] = 0, dfn[i] = 0,
low[i] = 0;
for(int i = 1; i <= n; i ++ )
add(i,a[i]);
for(int i = 1; i <= n; i ++ )
if(!dfn[i]) targan(i);
for(int i = 1; i <= scc_cnt; i ++ ) p[i] = i;
for(int i = 1; i <= n; i ++ )
for(int j = h[i]; ~j; j = ne[j]) {
int x = e[j];
if(id[x] == id[i]) continue;
dout[id[i]] ++;
if(find(id[i]) == find(id[x])) continue;
p[find(id[x])] = find(id[i]);
}
vector us[n + 1];
vector cs;
for(int i = 1; i <= scc_cnt; i ++ ) {
us[find(i)].pb(i);
if(p[i] == i) cs.pb(i);
}
bool flag = 1;
for(auto t1 : cs) {
bool ck = 0;
for(auto t2 : us[t1]) {
if(cnt[t2] == k && !dout[t2]) {
ck = 1;
break;
}
}
if(!ck) {
flag = 0;
break;
}
}
if(flag) cout << "Yes" << endl;
else cout << "No" << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int ts;
cin >> ts;
while(ts --) solve();
return 0;
}