Day8题解
本次题目来自:agc005c,arc080f,bzoj3319。
T1:豪迈
题意:给定 N N 以及一个长度为的数列 an a n ,求是否有一棵树满足第 i i 个点到树上最远的点的距离是。
题解:显然距离最远的两个点是树的直径上的两个端点,那么显然如果最大值的数量小于两个直接输出Impossible。
接着构造出直径,对直径长度分奇偶考虑(设直径长度为 len l e n ):
首先有一个结论:如果存在 ai<len2 a i < l e n 2 ,那么显然不存在这样的一棵树,因为如果 ai<len2 a i < l e n 2 ,那么显然它到另外一个端点的距离更大,不符合题意。
如果len是奇数,那么说明最小值( len2 l e n 2 )的点有两个,然后从最小值枚举到最大值,检查是否存在至少两个值,如果某个值不足两个,那么说明不存在(你连直径都构造不出来)。
偶数同理,不过最小值的点只有1个。
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int T;
int n,a[maxn],cnt[maxn],d;
int main()
{
T=read();
while(T--)
{
memset(cnt,0,sizeof(cnt));d=0;
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),cnt[a[i]]++,d=max(d,a[i]);
if(cnt[d]<2) {puts("Impossible");continue;}
bool flag=1;
if(d&1)
{
for(int i=d-d/2;i<=d;i++)
{
cnt[i]-=2;
if(cnt[i]<0) {flag=0;break;}
}
for(int i=d-d/2;i>=1;i--)
if(cnt[i]) {flag=0;break;}
}
else
{
if (!cnt[d/2]) flag=0;
if(flag)
{
cnt[d/2]--;
for (int i=d/2+1;i<=d;i++)
{
cnt[i]-=2;
if (cnt[i]<0){flag=0;break;}
}
for(int i=d/2;i>=1;i--)
if(cnt[i]) {flag=0;break;}
}
}
if(flag) puts("Possible");
else puts("Impossible");
}
return 0;
}
T2:性感ranwen在线翻身
题意:在一条数轴上有无限多的ranwen是趴着的,有 N N 个位置上的ranwen是躺着的,可以通过选择一个奇质数来翻转一段长度为 p p 的区间,求最少多少次可以把所有ranwen都变成趴着的。
题解:这题我不会,cky太强辣
考虑差分,如果一个位置与前一个位置不同,那么在这个位置设为1,也就是,这样一个有效的区间翻转就可以看做消去区间首位的两个1。
由哥德巴赫猜想可以得到以下结论:
1、如果区间长度是奇质数,那么只用翻转一次。
2、如果区间长度是偶数,那么需要翻转两次(大质数-小质数)。
3、如果区间长度是非奇质数,那么需要翻转三次(奇质数和偶数)。
所以我们就要优先找翻转一次的区间,再找翻转两次的区间,最后再找翻转三次的区间。
对奇数位置和偶数位置上的1建立二分图,先匹配长度为奇质数的,在匹配长度为偶数的,最后如果两边各剩一个则答案+3。
代码:
#include<bits/stdc++.h>
#define maxn 10000005
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,pos[maxn],prime[maxn],match[2005],tot;
bool vis1[2005],vis[maxn];
int a[1005],b[1005],cnt1,cnt2,ans;
vector<int> edge[1005];
bool dfs(int x)
{
for(int i=0;i<edge[x].size();i++)
if(!vis[edge[x][i]])
{
vis[edge[x][i]]=1;
if(!match[edge[x][i]] || dfs(match[edge[x][i]]))
{
match[edge[x][i]]=x;
return true;
}
}
return false;
}
void shai()
{
for(int i=2;i<=1e7;i++)
{
if(!vis1[i]) prime[++tot]=i;
for(int j=1;j<=tot && i*prime[j]<=1e7;j++)
{
vis1[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
vis1[1]=1;
}
int main()
{
shai();
n=read();
for(int i=1;i<=n;i++)
{
int x=read();
pos[x]=1;
}
for(int i=1;i<=1e7+1;i++)
if(pos[i]!=pos[i-1])
{
if(i&1)
a[++cnt1]=i;
else b[++cnt2]=i;
}
for(int i=1;i<=cnt1;i++)
for(int j=1;j<=cnt2;j++)
if(!vis1[abs(a[i]-b[j])])
edge[i].push_back(j);
for(int i=1;i<=cnt1;i++)
{
memset(vis,0,sizeof(vis));
ans+=dfs(i);
}
printf("%d\n",ans+(cnt1-ans)/2*2+(cnt2-ans)/2*2+((cnt1-ans)&1)*3);
return 0;
}
ranwen的服务器
题意:给定一棵树,有两种操作:
1 x,询问x到根节点路径上第一个遇到的黑边。
2 x y,将x到y这条路径上所有边染黑。
题解:显然树剖会TLE,而且还难写,所以我们需要转换思路。
考虑使用并查集,用倒着加边的方式来处理所有操作。
但这样做有个问题:因为是倒序的,所以有些边可能很早就被染黑了,但是在倒序处理是把它的染色时间推迟了。所以我们还需要正序来一遍来处理所有黑边被染黑的时间。
都使用并查集即可,正序处理时将黑边合并,用暴力lca处理,因为并查集维护所有黑边集合中深度最小的点,而lca时是跳并查集中的fa,因此每条边最多经过1次。
代码:
#include<bits/stdc++.h>
#define maxn 1000005
#define inf (1<<30)
#define mod 1000000007
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,m;
int head[maxn],to[maxn<<1],nex[maxn<<1],id[maxn<<1],cnt;
int deep[maxn],anc[maxn],bian[maxn];
int fa[maxn],tim[maxn],t[maxn];
int ans[maxn],tot;
struct node{
int opt,u,v;
}edge[maxn],q[maxn];
void add(int u,int v,int i)
{
to[++cnt]=v;nex[cnt]=head[u];id[cnt]=i;head[u]=cnt;
}
void dfs(int x,int la)
{
deep[x]=deep[la]+1;
for(int i=head[x];i;i=nex[i])
{
if(to[i]==la) continue;
edge[id[i]].u=x;edge[id[i]].v=to[i];
anc[to[i]]=x;
dfs(to[i],x);
bian[to[i]]=id[i];
}
}
void init(){for(int i=1;i<=n;i++) fa[i]=i;}
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
void modify(int u,int v,int ti)
{
u=getfa(u);v=getfa(v);
while(u!=v)
{
if(deep[u]<deep[v]) u^=v^=u^=v;
if(tim[u]==0) fa[u]=fa[anc[u]],tim[u]=ti;
u=fa[u];
}
}
void add_edge(int u,int v,int ti)
{
u=getfa(u);v=getfa(v);
while(u!=v)
{
if(deep[u]<deep[v]) u^=v^=u^=v;
if(tim[u]==ti) fa[u]=fa[anc[u]];
u=anc[u];
}
}
int main()
{
n=read();m=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read();
add(u,v,i);add(v,u,i);
}
dfs(1,0);init();
for(int i=1;i<=m;i++)
{
q[i].opt=read();q[i].u=read();
if(q[i].opt==2)
{
q[i].v=read();
modify(q[i].u,q[i].v,i);
}
}
init();
for(int i=2;i<=n;i++)
if(!tim[i])
{
int u=getfa(edge[bian[i]].u),v=getfa(edge[bian[i]].v);
fa[v]=u;
}
for(int i=m;i>=1;i--)
{
if(q[i].opt==1)
ans[++tot]=bian[getfa(q[i].u)];
else add_edge(q[i].u,q[i].v,i);
}
for(int i=tot;i>=1;i--)
printf("%d\n",ans[i]);
return 0;
}