출발한 노드의 visitTime과 도착한 노드의 visitTime을 비교했을 때, 출발한 노드의 visitTime이 더 크다면, 지금 도착한 노드는 이미 계산이 끝난 노드이므로 무시하고, 도착한 노드의 visitTime이 더 크다면 현재시간과 도착한 노드의 시간의 차이가 cycle의 크기가 된다.
따라서 시간복잡도는 $dp$ 테이블을 채우는데 $O(n)$, DFS를 하면서 cycle 부분의 색칠 수를 곱하는데에 $O(n)$ 나머지 트리 부분의 색칠 수를 곱하는데에 $O(n)$(이 부분은 $O(logn)$에도 가능하지만 bottleneck이 아니므로 무시)으로, 총 시간복잡도는 $O(n)$이다.
Time Complexity : $O(n)$
코드
#include<iostream>
#include<algorithm>
#define N 1000004
#define MOD 1000000007
using namespace std;
int n,k;
int a[N],t[N];
typedef long long ll;
ll dp[N];
int main()
{
cin.tie(NULL);
cin.sync_with_stdio(false);
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
dp[0]=k;
for(int i=2;i<=n;i++) dp[i] = (dp[i-1]*(k-2)+dp[i-2]*(k-1))%MOD;
dp[1]=k;
int c=1,cnt=0;
ll ans=1;
for(int i=1;i<=n;i++)
{
if(t[i]!=0)continue;
int j=i;
for(;t[j]==0; j=a[j]) t[j]=c++;
if(t[j]>=t[i])
{
int x = c-t[j];
ans = (ans*dp[x])%MOD;
cnt += x;
}
}
for(int i=n-cnt;i>0;i--) ans = (ans * (k-1))%MOD;
cout<<ans;
}
Given an arraynumsand a valueval, remove all instances of that valuein-placeand return the new length.
Do not allocate extra space for another array, you must do this bymodifying the input arrayin-placewith O(1) extra memory.
The order of elements can be changed. It doesn't matter what you leave beyond the new length.
풀이
two pointer를 이용하여 문제를 푼다.
선행 포인터가 val이랑 다를 경우 후행 포인터의 칸에 순차적으로 채워준다.
Time Complexity : $O(n)$
코드
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int i=0;
for(int j=0;j<nums.size();j++)
if(nums[j]!=val)nums[i++]=nums[j];
return i;
}
};
만약 $g_1,g_2$가 DFS spanning tree의 tree edge가 아니라면, $a,b$는 DFS spanning tree로 연결된다.
일반성을 잃지 않고 $g_1$이 $g_2$의 parent라고 하자.
이 때, $g_1, g_2$ edge가 단절선이 아니면, 즉 $g_2$에서 $g_2$위로 가는 bypass가 있으면 그 bypass로 연결이 가능하다.
만약 $g_1, g_2$가 단절선이라면, $a,b$가 단절선을 기준으로 같은 쪽에 있는지를 확인하고, 같은 쪽에 있으면 단절 후에도 연결이 가능하다.
위의 경우가 모두 불가능하다면, $a,b$ 사이의 경로는 단절선을 통하는 경로 뿐이고 단절선을 끊으면 이동할 수 없다.
각각의 체크는 모두 $O(1)$만에 가능하다.
2) 2번 쿼리, $a,b,c$가 입력으로 들어온다.
만약 $a,b$가 둘 모두 DFS spanning tree 상에서 $c$의 자손이 아니라면, tree의 정의에 의해 c를 통하지 않는 $a$와 $b$사이의 경로가 존재한다.
만약 둘 모두 $c$의 자손이라면, $c$의 자식들 중에서 $a$와 $b$를 각각 포함하는 자식이 존재할 것이다.
이를 $ac, bc$라고 하자. ($a=ac, b=bc$일 수도 있다.)
만약 $ac=bc$라면 $c$가 $ac, bc$의 lca(lowest common ancestor)가 아니라는 뜻이므로, c가 없어져도 a와 b사이의 경로가 존재한다.
만약 위의 경우가 아니라면 $a,b$중 $c$의 자손인 노드는 $c$를 우회해서 갈수 있는지 체크하고, 자손이 아닌 노드끼리는 단절의 전후 상관없이 항상 연결이 가능하므로 자손인 노드만 $c$보다 빠른 점으로 가는 bypass가 있는지 확인해주면 된다.
각각의 처리는 대부분 $O(1)$에 가능하지만, $ac,bc$를 찾기 위해서 $c$의 child 목록에서 binary search를 통해 해당하는 child를 찾아야하므로 $O(log(\text{# child of c}))$만큼의 시간복잡도가 필요하고, 이는 최대 $O(logn)$ 만큼 필요하다.
Time Complexity : $O(n+q_1+q_2logn)$
코드
#include<iostream>
#include<vector>
using namespace std;
struct data
{
int sTime;
int eTime;
int low;
int depth;
int par;
vector<int> adj;
vector<int> child;
};
vector<data> info;
int n,e,q;
int visitTime = 1;
void dfs(int now)
{
data& d=info[now];
d.sTime = d.low = visitTime++;
for(vector<int>::iterator it=d.adj.begin();it!=d.adj.end();++it)
{
int nxt = *it;
data& x = info[nxt];
if(x.depth!=-1)
{
if(d.par!=nxt) d.low = min(d.low, x.sTime);
continue;
}
x.depth = d.depth+1;
x.par = now;
d.child.push_back(nxt);
dfs(nxt);
d.low = min(d.low, x.low);
}
d.eTime = visitTime++;
}
bool isAncestor(int x1, int x2)
{
return info[x1].sTime<=info[x2].sTime && info[x2].eTime <=info[x1].eTime;
}
bool bypass(int x1,int x2)
{
return info[x1].low<info[x2].sTime;
}
int findChild(int x1, int x2)
{
int l=0,r=info[x1].child.size()-1;
while(l!=r)
{
int mid = (l+r)>>1;
int now = info[x1].child[mid];
if(info[x2].sTime > info[now].eTime) l=mid+1;
else if(info[x2].eTime < info[now].sTime) r = mid-1;
else return now;
}
return info[x1].child[l];
}
void yes()
{
cout<<"yes\n";
}
void no()
{
cout<<"no\n";
}
int main()
{
cin.tie(NULL);
cin.sync_with_stdio(false);
cin>>n>>e;
data init={0,0,0,-1,0,vector<int>(),vector<int>()};
info.resize(n+1,init);
while(e--)
{
int x,y;
cin>>x>>y;
info[x].adj.push_back(y);
info[y].adj.push_back(x);
}
info[1].par = -1;
info[1].depth = 1;
dfs(1);
cin>>q;
while(q--)
{
int x,a,b;
cin>>x>>a>>b;
if(x==1)
{
int g1,g2;
cin>>g1>>g2;
if(isAncestor(g2,g1)) swap(g1,g2);
if(info[g1].depth != info[g2].depth-1) yes();
else if(bypass(g2,g2)) yes();
else if(isAncestor(g2,a)==isAncestor(g2,b)) yes();
else no();
}
else
{
int c;
cin>>c;
if(!isAncestor(c,a) && !isAncestor(c,b)) yes();
else if(isAncestor(c,a) && isAncestor(c,b))
{
int ac = findChild(c,a);
int bc = findChild(c,b);
if(ac==bc)yes();
else if(bypass(ac,c)&&bypass(bc,c)) yes();
else no();
}
else
{
if(!isAncestor(c,a)) swap(a,b);
int ac = findChild(c,a);
if(bypass(ac,c)) yes();
else no();
}
}
}
}