d.Find the Different Ones!
#include<iostream>
#include<cstring>
using namespace std;
//这个题的关键就是要想到 如果这个区间里有不同的数 那么一定会有与a[l]不同的数
//直接看a[l]的最近的不同的数有没有出现在区间中即可
int t;
int a[1000009];
int d[1000009];
int main()
{
cin>>t;
while(t--)
{
memset(a,0,sizeof(a));
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n;i>1;i--)
{
if(a[i]!=a[i-1])
d[i-1] = i;
else d[i-1] = d[i];
}
int q;
cin>>q;
while(q--)
{
int l,r;
cin>>l>>r;
if(r>=d[l]&&d[l])
cout<<l<<" "<<d[l]<<endl;
else cout<<-1<<" "<<-1<<endl;
}
}
return 0;
}
e.Klever Permutation
#include<iostream>
#include<cstring>
using namespace std;
//这个题最关键的是要想到怎样可以使k连续的数的和之间的差不超过1
//实现这个的直接方法就是让第i个数比第i-k个数大1或小1
//并且保证是对于前一个数是大1 对于后一个数就要是小1 不然前后的和之间的差就会超过1
//我的想法就是奇数位从小到大递增 偶数位从大到小递减
int t;
int a[200009];
int main()
{
cin>>t;
while(t--)
{
memset(a,0,sizeof(a));
int n,k;
cin>>n>>k;
int s=1,b=n; //排数列的数 s是从小到大的奇数位 b是从大到小的偶数位
int cent = 0;
int i=1; //有点类似于把n分成k个区间 每次填每个区间里的一个数 i就是这个数在区间中的位数
while(cent<n)
{
if(i%2==1) //如果是奇数位
{
for(int j=i;j<=n;j+=k) //每隔k位填一次 每次加1
{
a[j] = s;
s ++;
cent ++;
}
}else //如果是偶数位
{
for(int j=i;j<=n;j+=k) //每隔k位填一次 每次减1
{
a[j] = b;
b --;
cent ++;
}
}
i ++; //下一个区间位置
}
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
}
return 0;
}
f.Microcycle
这个题比较难,用到的知识点比较多
暴力的做法肯定就是从最小边开始一个一个的试能不能成环
时间复杂度大概是o(n^2)
关键就在于怎么准确的找到这个最小边 只进行一次dfs找环
用到的就是kruskal算法 利用并查集 找到非树边中的最小边
这里需要将边从大到小排序 将所有树边排除 找到最后一个非树边 就是成环中的最小边
这里不是从小到大排序,如果是从小到大,形成的环不一定包括前面的最小边,而从大到小,则保证了最后的最小非树边,一定包括在环中。
将这个最小边的两端 作为起点和终点,进行dfs,查找路径
#include<iostream>
#include<algorithm>
using namespace std;
int t,n,m;
struct edge
{
int u,v,w;
} e[200009];
bool cmp(struct edge e1,struct edge e2)
{
return e1.w>e2.w;
}
int fa[200009];
int find(int x)
{
if(x==fa[x]) return x;
return fa[x] = find(fa[x]);
}
//利用kruskal从大到小 建立最小生成树 最后一个不符合条件的非树边 就是最小边
//从小到大的话 可能形成的环并不包括最小边
int find_min()
{
int num;
for(int i=1;i<=n;i++) fa[i] = i;
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++)
{
int x = find(e[i].u);
int y = find(e[i].v);
if(x==y)
num = i; //非树边中的最小边
else fa[x] = y;
}
return num;
}
int beg,last,cent;
int ans[200005];
int first[200005],vis[200009];
struct node
{
int to,next;
}e2[200009*2];
void add(int u,int v)
{
cent ++;
e2[cent].to = v;
e2[cent].next = first[u];
first[u] = cent;
}
int sum ;
int flag = 0;
//以找到的最小边的两端 进行dfs 找到路径
void dfs(int a,int num)
{
if(flag) return;
ans[num] = a;
//cout<<ans[num]<<" "<<num<<endl;
if(a==last)
{
flag = 1;
sum = num;
return;
}
for(int i=first[a];i;i=e2[i].next)
{
int to = e2[i].to;
if(num==1&&to==last||vis[to]==1) continue;
vis[to] = 1; //不能溯洄 如果溯洄会超时相当于遍历了整个图 肯定不用走之前走过的路 如果需要的话第一走就可以
dfs(to,num+1); //第一次走发现到不了 之后也不需要经过这个点
}
}
int main()
{
cin>>t;
while(t--)
{
for(int i=1;i<=n;i++)
{
ans[i] = 0;
vis[i] = 0;
first[i] = 0;
}
beg=0,last=0,cent=0;
sum = 0,flag = 0;
scanf("%d%d",&n,&m);
//cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
//cin>>e[i].u>>e[i].v>>e[i].w;
}
int minnum = find_min();
beg = e[minnum].u;
last = e[minnum].v;
//cout<<last<<" last"<<endl;
for(int i=1;i<=m;i++)
{
add(e[i].u,e[i].v);
add(e[i].v,e[i].u);
}
vis[beg] = 1;
dfs(beg,1);
printf("%d %d\n",e[minnum].w,sum);
//cout<<e[minnum].w<<" "<<sum<<endl;
for(int i=1;i<=sum;i++)
printf("%d ",ans[i]);
//cout<<ans[i]<<" ";
}
return 0;
}
g题太难了,本菜鸟还还看不懂……