A
发现当三个数都一样时答案是无穷,否则直接模拟就好了,步数不会太多(可以证明最多log次)
B
模型转化,在mod 2意义下,x到y+1,和 根到x+1根到y+1是等效的,所以每个点出现次数都是偶数则有解,否则某些点的父边会是奇数,无解
C
一开始走完k步后,可以开k步走k步,相当于图中没有障碍了,所以bfs一下一开始k步能到哪些点,然后枚举这些点看他们至少操作多少次到达边界
D
当最终的树上有某个白点不和黑点相连,先手赢,那么度数越小的白点越容易成为答案
先考虑叶子节点,先手先取最深的某个叶子节点的父亲,后手一定要取这个叶子,这时如果这个父亲还有其他叶子孩子则先手直接胜利,否则相当于从原树中去掉了这两个点
感觉根可以随便定,bfs一遍后,按深度从大到小染色,去掉这些成对的点,染完还要额外判一下有没有不与黑点相连的点(有特殊情况)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
int n,m,d[maxn];
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
int fa[maxn],dep[maxn];
void dfs(const int x)
{
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa[x])
dep[y]=dep[x]+1,fa[y]=x,dfs(y);
}
int col[maxn];
struct node{int d,x;};
inline bool operator <(const node x,const node y){return x.d<y.d;}
priority_queue<node>q;
bool Col()
{
for(int i=1;i<=n;i++) q.push((node){dep[i],i});
memset(col,-1,sizeof col);
while(!q.empty())
{
const node now=q.top(); q.pop();
int x=now.x;
if(col[x]!=-1) continue;
if(x==1) {col[x]=0; break;}
if(col[fa[x]]!=-1) return true;
col[fa[x]]=0;
col[x]=1;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) d[a[k].y]++;
}
return false;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y; scanf("%d%d",&x,&y);
ins(x,y); ins(y,x);
}
dep[1]=1; dfs(1);
if(Col()) return puts("First"),0;
for(int i=1;i<=n;i++) if(!col[i]&&!d[i]) return puts("First"),0;
puts("Second");
return 0;
}
E
考虑一次操作,先删掉一条原树的边,此时树变成了两个联通块,然后添加一条连接这两个联通块的新边,然后对这2个联通块内部进行操作,如果能操作n-1次就合法,最后一次操作肯定是删掉两点间的边再添加回去
我们倒着考虑这个过程,就是每次选一对连接相同点的边,将这两个点合并成一个点,因为对于一棵树,两个联通块之间至多只有一条边相连,所以两棵树至多2条,所以不需要考虑边的颜色
于是维护一个multiset存连接相同点的边对,每个点开multiset存和他相连的点,再开一个map[x][y]存x和y之间的边数,每次合并点时启发式合并,nlog^2n
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
int n,m;
struct edge{int x,y;edge(){}edge(const int _x,const int _y){x=_x;y=_y;}};
inline bool operator <(const edge x,const edge y){return x.x==y.x?x.y<y.y:x.x<y.x;}
map<int,int>mp[maxn];
multiset<edge>q;
multiset<edge>::iterator it;
multiset<int>S[maxn];
multiset<int>::iterator it2;
int num[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) num[i]=1;
for(int i=1;i<=2*(n-1);i++)
{
int x,y; scanf("%d%d",&x,&y);
mp[x][y]++; mp[y][x]++;
if(mp[x][y]==2) q.insert(x<y?edge(x,y):edge(y,x));
S[x].insert(y); S[y].insert(x);
}
for(int i=1;i<n;i++)
{
it=q.begin(); if(it==q.end()) return puts("NO"),0;
int x=(*it).x,y=(*it).y; q.erase(it);
if(num[x]<num[y]) swap(x,y);
num[x]+=num[y];
for(it2=S[y].begin();it2!=S[y].end();it2++)
{
int k=(*it2); if(k==x) { S[x].erase(S[x].find(y));continue; }
if(mp[y][k]==2) q.erase(q.find(y<k?edge(y,k):edge(k,y)));
mp[y][k]--; mp[k][y]--;
S[k].erase(S[k].find(y));
if(mp[x].count(k)>0&&mp[x][k]==2) return puts("NO"),0;
mp[x][k]++; mp[k][x]++;
S[x].insert(k); S[k].insert(x);
if(mp[x][k]==2) q.insert(x<k?edge(x,k):edge(k,x));
}
}
puts("YES");
return 0;
}
F
发现1对2~n的排序没影响,设将2~n的序列排至有序需要T2次,对原序列操作T2次后,若1在开头则T1=T2,否则T1=T2+1
考虑对2~n序列操作T2-1后的序列,设开头是fi,显然fi>2,否则T2-1+1次操作后不能使2~n变得有序,或T2-1次后已经有序
这时,若1在fi和2之间,T1=T2,否则T1=T2+1,这里有个性质,(fi,1,2)的顺序旋转0~2次和最开始的顺序相同
证明:
先证明fi只有在序列开头时才是high element其他情况都是low:若fi不在开头时成为了high,则前面一定有某个y< fi是high,排一次后y和fi挨着,fi要成为开头,一定要和y分离,若分离,只能是y为low,fi为high,这时候前面一定存在某个y’< fi,回到原来的状况,因此fi永远不能成为开头,反证得到若fi是T2-1次的开头,当且仅当fi是开头时fi是high
然后分类讨论fi,1,2在序列中的位置,证明i+1->i时这个性质成立,归纳得(fi,1,2)圆排不变这个性质成立
于是可以通过原序列的位置关系判断是否满足1在fi和2中间 于是可以由Tn递推到T1,维护fi,复杂度O(n)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
int n;
int a[maxn],p[maxn];
int T[maxn],f[maxn];
bool judge(const int x,const int y,const int z){return x<y?(z<x||z>y):(y<z&&z<x);}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),p[a[i]]=i;
T[n]=0;
for(int i=n-1;i>=1;i--)
{
if(!T[i+1])
{
if(p[i]<p[i+1]) T[i]=0;
else T[i]=1,f[i]=i+1;
}
else
{
if(judge(p[f[i+1]],p[i],p[i+1])) T[i]=T[i+1],f[i]=f[i+1];
else T[i]=T[i+1]+1,f[i]=i+1;
}
}
printf("%d\n",T[1]);
return 0;
}