Problem A. Christmas Tree
LCA+dfs序
个一个染色操作过程,求每次染色后所有染色的点的LCA。
用dfs访问循序记录每一个点,然后每次每次计算顺序最大与顺序最小的点的LCA即可。
(数据量较大使用printf和scanf较好)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<set>
using namespace std;
const int maxn=100005;
struct Edge{
int from;
int to;
int next;
Edge(int _from=0,int _to=0,int _next=-1):from(_from),to(_to),next(_next){
}
}Edges[maxn<<1];
int cnt;
int head[maxn];
int num[maxn];
int renum[maxn];
int T;
int n,m;
set<int>st;
void addEdge(int a,int b){
Edges[cnt].from=a;
Edges[cnt].to=b;
Edges[cnt].next=head[a];
head[a]=cnt;
cnt++;
Edges[cnt].from=b;
Edges[cnt].to=a;
Edges[cnt].next=head[b];
head[b]=cnt;
cnt++;
}
int father[maxn][31];
int depth[maxn];
void bfs(int s){
memset(depth,0x3f,sizeof(depth));
queue<int>Q;
Q.push(s);
depth[s]=0;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=head[u];i!=-1;i=Edges[i].next){
Edge &e=Edges[i];
if(depth[e.to]>depth[u]+1){
father[e.to][0]=u;
depth[e.to]=depth[u]+1;
Q.push(e.to);
for(int k=1;k<=16;k++){
father[e.to][k]=father[father[e.to][k-1]][k-1];
}
}
}
}
}
int lca(int a,int b){
if(depth[a]<depth[b]){
swap(a,b);
}
for(int i=16;i>=0;i--){
if(depth[father[a][i]]>=depth[b]){
a=father[a][i];
}
}
if(a==b){
return a;
}
for(int i=16;i>=0;i--){
if(father[a][i]!=father[b][i]){
a=father[a][i];
b=father[b][i];
}
}
return father[a][0];
}
int l;
void dfs(int u){
num[u]=l;
renum[l]=u;
l++;
for(int i=head[u];i!=-1;i=Edges[i].next){
Edge &e=Edges[i];
if(father[e.to][0]==u){
dfs(e.to);
}
}
}
int main(){
scanf("%d",&T);
for(int kase=1;kase<=T;kase++){
st.clear();
cnt=0;
l=0;
memset(father,0,sizeof(father));
memset(depth,0,sizeof(depth));
memset(head,-1,sizeof(head));
memset(num,-1,sizeof(num));
scanf("%d",&n);
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
addEdge(a,b);
}
bfs(0);
dfs(0);
char paint[2];
int id;
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%s %d",paint,&id);
if(paint[0]=='+'){
st.insert(num[id]);
}else{
st.erase(num[id]);
}
set<int>::iterator it;
if(!st.empty()){
it=st.end();
//cout<<lca(renum[*(st.begin())],renum[*(--it)])<<endl;
printf("%d\n",lca(renum[*(st.begin())],renum[*(--it)]));
}else{
printf("-1\n");
//cout<<-1<<endl;
}
//printf("%s %d\n",paint,id);
}
}
return 0;
}
Problem B. The Lion King
Problem C. Cultivating Mars
Problem D. Fractionstellar
数论GCD+模拟
由公式最简假分数的GCD,可以得知分数的最小公约数为分子的最小公倍数除以分母的最大公约数;最大公约数等于分子的最大公约数除以分母的最小公倍数。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
unsigned long long gcd(unsigned long long A,unsigned long long B){
if(B==0)return A;
if(A==0)return B;
if(A==B)return A;
if(B>=A)return gcd(B%A,A);
else return gcd(A%B,B);
}
unsigned long long lcm(unsigned long long A,unsigned long long B){
return (A*B)/gcd(A,B);
}
unsigned long long a,b,c,d;
int T;
int main(){
scanf("%d",&T);
for(int kase=1;kase<=T;kase++){
cin>>a>>b>>c>>d;
unsigned long long g1=gcd(a,b);
//b/=gcd(a,b);
//c/=gcd(c,d);
unsigned long long g2=gcd(c,d);
a/=g1;
b/=g1;
c/=g2;
d/=g2;
if(a==c&&b==d){
cout<<a<<"/"<<b<<" "<<c<<"/"<<d<<endl;
}else {
unsigned long long G1=gcd(a,c);
unsigned long long L1=lcm(a,c);
unsigned long long G2=gcd(b,d);
unsigned long long L2=lcm(b,d);
cout<<(G1/gcd(G1,L2))<<"/"<<(L2/gcd(G1,L2))<<" "<<(L1/gcd(L1,G2))<<"/"<<(G2/gcd(G2,L1))<<endl;
}
}
return 0;
}
Problem E. The Minions Quiz
贪心
比赛的时候读错题以为求最小值,结束后才发觉是求最大值。贪心的策略先全&后全|,因为如果先&后|交换&和|的顺序,每一位如果是0则不可能变成1,而1却有可能变成0,因此只可能会变小,所以这样的策略是最优的。
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=10004;
long long a[maxn];
int T;
int n;
int A,B;
int main(){
scanf("%d",&T);
for(int kase=1;kase<=T;kase++){
scanf("%d%d",&A,&B);
for(int i=1;i<=A+B+1;i++){
scanf("%lld",&a[i]);
}
long long ans=a[1];
for(int i=1;i<=A+1;i++){
ans&=a[i];
}
for(int i=A+2;i<=A+B+1;i++){
ans|=a[i];
}
cout<<ans<<endl;;
}
return 0;
}
Problem F. Uberfication
Problem G. DNA Evolution
Problem H. Bingo!
Problem I. Journey
Problem J. Banana
字符串
签到题,暴力字符串替换即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
char dic[101][21];
char key[101][21];
char sentence[101][21];
int N,T;
char temp[21];
int n;
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++){
scanf("%s",key[i]);
scanf("%s",temp);
scanf("%s",dic[i]);
}
/* for(int i=1;i<=N;i++){
cout<<dic[i]<<" "<<key[i]<<endl;
}*/
scanf("%d",&T);
for(int i=1;i<=T;i++){
scanf("%d",&n);
for(int j=1;j<=n;j++){
scanf("%s",sentence[j]);
}
for(int j=1;j<=n;j++){
bool flag=true;
for(int k=1;k<=N;k++){
if(strcmp(sentence[j],key[k])==0){
printf("%s ",dic[k]);
flag=false;
break;
}
}
if(flag)printf("%s ",sentence[j]);
}
printf("\n");
}
return 0;
}
Problem K. Road Network
树的直径
模板题,加一条边形成的圈上的所有边都不是所求的边,因此问题转化为找树上的最长路径即树的直径。一般可以用两次dfs()或树上dp解决,由于太弱不会dp,就采用两次dfs()。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=10004;
struct Edge{
int from;
int to;
Edge(int _from=0,int _to=0):from(_from),to(_to){
}
}Edges[maxn<<1];
vector<int>G[maxn];
int cnt;
void addEdge(int a,int b){
Edges[cnt].from=a;
Edges[cnt].to=b;
G[a].push_back(cnt);
cnt++;
Edges[cnt].from=b;
Edges[cnt].to=a;
G[b].push_back(cnt);
cnt++;
}
int dis[2][maxn];
int l[2];
int q[2];
int T;
int n;
void dfs(int mark,int u,int d){
dis[mark][u]=d;
for(int i=0;i<G[u].size();i++){
Edge &e=Edges[G[u][i]];
if(dis[mark][e.to]==-1){
dfs(mark,e.to,d+1);
if(l[mark]<dis[mark][e.to]){
l[mark]=dis[mark][e.to];
q[mark]=e.to;
}
}
}
}
void init(int o){
l[0]=l[1]=0;
cnt=0;
memset(dis,-1,sizeof(dis));
for(int i=1;i<=n;i++)G[i].clear();
}
int main(){
scanf("%d",&T);
for(int kase=1;kase<=T;kase++){
scanf("%d",&n);
init(n);
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
addEdge(a,b);
}
dfs(0,1,0);
dfs(1,q[0],0);
printf("%d\n",n-1-l[1]);
}
return 0;
}