导语
题目很难,太刺激了属于是
涉及的知识点
图,字符串,思维
链接:16 HK
题目
A
题目大意:定义k色图为一张有n个节点的无向图,每个节点是k种颜色中的一种,不要求相邻相异,现在给出两个k色图s和t,要将s变化到t,变化的规则如下:每一步,每一个点可以选择变化成一个相邻节点的颜色或者保持现在的颜色,给出初始的s和t,判断s是否能到达t
思路:把这个题目想复杂了,一共有100个点,全交换也就1e4的数据量,也就是1e4步,但是题设条件给了20000步,又因为是连通图,所以如果初始图各颜色数量与终点图各颜色数量相同,那么最后初始图一定可以到达终点图,只需要暴力构造模拟即可
代码
#include<bits/stdc++.h>
using namespace std;
const int maxl=110;
int n,m,k,flag,ans,ansv;
int a[maxl],ta[maxl],num[maxl],tnum[maxl];
int frm[maxl];
bool vis[maxl];
vector<int> e[maxl],ee[maxl],b;
inline void predfs(int u) {
vis[u]=true;
for(int v:ee[u])
if(!vis[v]) {
e[u].push_back(v);//存相邻点
e[v].push_back(u);
predfs(v);
}
b.push_back(u);//dfs序
}
inline void prework() {
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=n; i++)//记录一开始图中颜色数
scanf("%d",&a[i]),num[a[i]]++;
for(int i=1; i<=n; i++)
scanf("%d",&ta[i]),tnum[ta[i]]++;//记录终了图中颜色数
int u,v;
for(int i=1; i<=m; i++) {//存图
scanf("%d%d",&u,&v);
ee[u].push_back(v);
ee[v].push_back(u);
}
predfs(1);
}
inline void print() {//输出过程
for(int i=1; i<=n; i++)
printf("%d%c",a[i]," \n"[i==n]);
}
inline void dfs(int u) {
vis[u]=true;//标记访问
if(num[a[u]]>tnum[a[u]]) {//找到一个颜色起始比终了多的点
ansv=u;//记录这个点
return;
}
for(int v:e[u])
if(!vis[v]) {
frm[v]=u;
dfs(v);
if(ansv) return;
}
}
inline void findcol(int u,int col) {
vis[u]=true;
if(a[u]!=ta[u] && a[u]==col) {//找到一个颜色为col并且当前颜色不与终点相同的点
ansv=u;//记录下标
return;
}
for(int v:e[u])
if(!vis[v]) {
frm[v]=u;
findcol(v,col);
if(ansv) return;
}
}
inline void mainwork() {
ans=1;
for(int i=0; i<k; i++)
if(tnum[i]>0 && num[i]==0)
ans=0;
if(!ans) {//变不出来
puts("Impossible");
return;
}
print();//输出初始图
int u,v;
for(int i=0; i<k; i++)
if(num[i]<tnum[i]) {//找到一种需要增加的颜色i
for(int j=1; j<=n; j++)
if(a[j]==i) {//找到一个是该颜色的点j
u=j;
break;
}
while(num[i]<tnum[i]) {
for(int j=1; j<=n; j++)//清空父标记与访问标记
frm[j]=0,vis[j]=false;
ansv=0;
dfs(u);
v=ansv;//记录一个多余的点
while(frm[v]!=u) {//把这个多余的点换到j的邻接点
swap(a[v],a[frm[v]]);
v=frm[v];
print();
}
num[a[v]]--;
num[i]++;
a[v]=i;//把这个点涂成需要增加的颜色
print();
}
}
for(int d:b)
if(a[d]!=ta[d]) {//如果起始点颜色与终点不同
for(int i=1; i<=n; i++)//初始化
frm[i]=0,vis[i]=false;
ansv=0;
findcol(d,ta[d]);//找到一个与终点颜色相同的可达点
v=ansv;
while(v!=d) {//把找到的点换过去
swap(a[v],a[frm[v]]);
v=frm[v];
print();
}
}
}
int main() {
prework();
mainwork();
return 0;
}
C
题目大意:有N个数字,每个数字组成都是 2 a 3 b 2^a3^b 2a3b的形式,现在要对这些数字进行N-1个操作,每个操作选择两个数 X , Y X,Y X,Y,将这两个数合并成 o p ( X , Y ) op(X,Y) op(X,Y),多次操作之后,只剩下一个数字每次操作是 g c d gcd gcd或 l c m lcm lcm,现在有N个阶段,每个阶段可以操作 g c d gcd gcdk次然后操作 l c m lcm lcmN-1-k次,对每个阶段,求出剩下的最大值与最小值
思路:第一个和最后一个阶段的结果都是定值,对于一般情况,如果想要值最大,需要至少把a和b最多的两个数进行 l c m lcm lcm,其余的数随意,如果想要最小,需要把a和b最小的两个数进行 g c d gcd gcd然后将结果与其他数随意后的结果gcd即可,注意特殊情况的判断
代码
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N = 5e4+10;
struct num {
int a,b;
} da[N];
int maxa1[N],mina1[N],maxb1[N],minb1[N];
bool com(num a,num b) {
return (a.a*log(2)+a.b*log(3)<b.a*log(2)+b.b*log(3));
}
void solve() {
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d%d",&(da[i].a),&(da[i].b));
sort(da+1,da+1+n,com);
maxa1[1]=mina1[1]=da[1].a;
maxb1[1]=minb1[1]=da[1].b;
for(int i = 2; i <= n; i++) {
maxa1[i] = max(maxa1[i-1],da[i].a);
mina1[i] = min(mina1[i-1],da[i].a);
maxb1[i] = max(maxb1[i-1],da[i].b);
minb1[i] = min(minb1[i-1],da[i].b);
}
if(n==1)
printf("%d %d %d %d\n",maxa1[n],maxb1[n],maxa1[n],maxb1[n]);
if(n==2) {
printf("%d %d %d %d\n",maxa1[n],maxb1[n],maxa1[n],maxb1[n]);
printf("%d %d %d %d\n",mina1[n],minb1[n],mina1[n],minb1[n]);
}
if(n==3) {
printf("%d %d %d %d\n",maxa1[n],maxb1[n],maxa1[n],maxb1[n]);
printf("%d %d %d %d\n",da[n].a,da[n].b,da[1].a,da[1].b);
printf("%d %d %d %d\n",mina1[n],minb1[n],mina1[n],minb1[n]);
}
if(n>=4) {
printf("%d %d %d %d\n",maxa1[n],maxb1[n],maxa1[n],maxb1[n]);
printf("%d %d %d %d\n",maxa1[n],maxb1[n],da[1].a,da[1].b);
for(int i = 3; i < n-1; i++)
printf("%d %d %d %d\n",maxa1[n],maxb1[n],mina1[n],minb1[n]);
printf("%d %d %d %d\n",da[n].a,da[n].b,mina1[n],minb1[n]);
printf("%d %d %d %d\n",mina1[n],minb1[n],mina1[n],minb1[n]);
}
}
int main() {
int t = 1;
while(t--)
solve();
return 0;
}
J
题目大意:给出n个01串,求出一个最长的01串s使得这n个01串都不为s的子串
思路:直接构造AC自动机,用DFS查找构造满足条件的字符串并且标记已经访问过的节点,如果在搜索过程中访问到已标记,代表可以组成无限个,有个与基本AC自动机不同的地方:对于一个节点,当需要向下遍历时,需要判断当前节点+一个子节点是否会匹配了输入串,需要从子的fail出发一直向下,如果碰到一个节点是串的结尾说明会匹配输入串,则该子节点不可选,那么反向推理,如果一个节点x是一个串的结尾且该节点x又是另一个节点y的失配位置,那么显然y节点到x的这条道也应当不可选了
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=4e5+10;
int trie[maxn][2],End[maxn],fail[maxn],n,cnt,length;
bool vis[maxn];
char s[maxn],res[2][maxn];
void Insert() {
int p=0,len=strlen(s);
for(int i=0; i<len; i++) {
int ch=s[i]-'0';
if(!trie[p][ch])
trie[p][ch]=++cnt;
p=trie[p][ch];
}
End[p]++;
}
void Build() {
queue<int>Q;
for(int i=0; i<2; i++)
if(trie[0][i])//预处理第一层
Q.push(trie[0][i]);
while(!Q.empty()) {
int t=Q.front();
Q.pop();
for(int i=0; i<2; i++)
if(trie[t][i]) {//如果不为空
fail[trie[t][i]]=trie[fail[t]][i];
End[trie[t][i]]+=End[trie[fail[t]][i]];
//这里如果在t这里失配但是在t的失配指针匹配,也算t能够匹配
Q.push(trie[t][i]);
} else
trie[t][i]=trie[fail[t]][i];
}
}
bool DFS(int x,int dep) {
if(vis[x])return 1;//如果访问过,代表有环
vis[x]=1;
bool flag,t=0;
for(int i=0; i<2; i++) {
flag=1;
int id=trie[x][i];//获得下一位置
if(id&&End[id]>0)flag=0;//如果不为空且存在匹配,不能取
if(flag) {
res[0][dep]=i+'0';//构造
if((flag=DFS(id,dep+1)))return 1;//继续构造,存在环返回
}
t|=flag;//记录是否有环
}
if(!t&&dep>length) {//无环且比当前字符串长度长
length=dep;
for(int j=0; j<length; j++)res[1][j]=res[0][j];
}
vis[x]=0;
return flag;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>n;
for(int i=1; i<=n; i++) {
cin >>s;
Insert();//字典树插入
}
Build();//建立AC自动机
if(DFS(0,0))//搜索字符串
cout <<-1;
else
for(int i=0; i<length; i++)cout <<res[1][i];//输出结果
return 0;
}