20221103
20221103
easy
题意理解
很容易想到割边(或者说桥)和强连通分量,因为割边一定得是无向边,否则就可能使割边两端的其中一个点集无法通过割边到达另一点集;并且强连通分量中的点可以从其中任意一点,到达该强连通分量任意的另一点。所以我们只需要让图中的所有环变为强连通分量,并且使其中的边朝向相同(即首尾相接),然后让所有割边变为无向边即可。
代码可能比较丑陋,因为本人的 \(tarjan\) 学的不是特别好,所以实现将无向边转化为有向边的方式不太一样。代码中的边权 \(e[i].w\) ,就是用来实现这个操作的。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?~x+1:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int N=1000005;
int n,m;
int pre[N];
struct edge{
int v,w,nex,ans;
}e[N<<1];
int head[N],tot=1;
inline void add(int u,int v,int w){e[++tot].v=v,e[tot].w=w,e[tot].nex=head[u],head[u]=tot;}
inline int miN(int a,int b){return a<b?a:b;}
int dfn[N],low[N],cnt,st[N],top,ste[N<<1],tope;
bitset<N>ins;
bitset< N<<1 > brige;
inline void tarjan(int x){
low[x]=dfn[x]=++cnt;
st[++top]=x,ins[x]=1;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].v;
if(!e[i].w)continue;
if(!dfn[v]){
e[i^1].w=0;
tarjan(v);
low[x]=miN(low[x],low[v]);
if(low[v]>dfn[x])brige[i]=brige[i^1]=1;
ste[++tope]=i;
}
else if(ins[v])low[x]=miN(low[x],dfn[v]),e[i^1].w=0,ste[++tope]=i;
}
if(dfn[x]==low[x]){
int v;
do{
v=st[top--],ins[v]=0;
}while(x!=v);
while(tope){
if(brige[ste[tope]]){
e[ste[tope]].ans=e[ste[tope]^1].ans=0;
--tope;
continue;
}
e[ste[tope]].ans=1;
e[ste[tope]^1].ans=2;
--tope;
}
}
}
int p,q,what[N];
int main(){
in(n),in(m);
fo(i,1,m){
in(p),in(q);
add(p,q,1);
what[i]=tot;
add(q,p,1);
}
tarjan(1);
fo(i,1,m)out(e[what[i]].ans);
return 0;
}
木积
思路
乍一看好像没什么思路,一看数据范围 \(a_i<1000\) 肯定有问题,再看发现有针对质因子的部分分。自然而然地就往质因数那方面去想。很容易发现,能将两道题目放在一起的条件其实就是它们没有相同的质因子。再结合 \(a_i\) 的数据范围,推一推,发现 \(37^2\) 就已经大于 \(1000\) 了,所以 \(a_i\) 中大于等于 \(37\) 的质因子最多只有一个,而小于 \(37\) 的质数只有 \(11\) 个,这显然可以根号分治。
根号分治
说白了,其实就是将大于 \(\sqrt {1000}\) 的质因子与小于 \(\sqrt{1000}\) 的质因子分开处理。先将可以被状压下来的小于 \(37\) 的质因子进行转移,再将更大的质因子单独进行转移。
对于可以状压的部分,只需判断是否有相同质因子,就可进行转移,即判断 \(st1\) &
\(st2\) 是否为 \(0\) ( \(st1,st2\) 表示质因子状压后的状态)。不为 \(0\) 表示有相同的质因子。为 \(0\) 就进行转移 \(dp[st1|st2]=max(dp[st1|st2],dp[st2]+1)\)
对于不可状压的部分,只需对每个有该大质因子的数进行转移即可。
这里有个小问题,就是在每次转移前都得将上一次的 \(dp\) 值全部复制过来,如果不这么做的话,就可能会导致 \(dp[i-2][st1]\) 在转移过程中因为只有 \(st1&st2!=0\) 时才进行转移,而没有被转移到 \(dp[i-1]\) 中,导致 \(dp[i]\) 可能需要通过 \(dp[i-2]\) 转移,但这样就无法做到。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?~x+1:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int N=1005,maxn=1<<11;
int a[N];
int st[N],prim[]={2,3,5,7,11,13,17,19,23,29,31};
int dp[N][maxn],p;
vector<int> large[N];
bitset<N>vis;
int n;
inline int maX(int a,int b){return a>b?a:b;}
int ans;
inline void solve(){
fo(i,1,n){
fo(j,0,10){
if(a[i]%prim[j]==0)st[i]|=(1<<j);
while(a[i]%prim[j]==0)a[i]/=prim[j];
}
if(a[i]>1)vis[i]=1,large[a[i]].push_back(i);
}
fo(i,1,n){
if(vis[i])continue;
++p;
memcpy(dp[p],dp[p-1],sizeof dp[p-1]);
fo(j,0,maxn-1){
if((st[i]&j)!=0)continue;
dp[p][j|st[i]]=maX(dp[p][j|st[i]],dp[p-1][j]+1);
}
}
fo(i,2,1000){
if(!large[i].size())continue;
++p;
memcpy(dp[p],dp[p-1],sizeof dp[p-1]);
for(int j=0;j<large[i].size();++j){
int v=large[i][j];
fo(k,0,maxn-1){
if((st[v]&k)!=0)continue;
dp[p][k|st[v]]=maX(dp[p][k|st[v]],dp[p-1][k]+1);
}
}
}
fo(i,0,maxn-1)ans=maX(ans,dp[p][i]);
out(ans),putchar('\n');
}
inline void init(){
memset(dp,0,sizeof dp);
p=0;
fo(i,0,1000)large[i].clear(),st[i]=0;
vis.reset();
ans=0;
}
int main(){
int t;
in(t);
while(t--){
in(n);
fo(i,1,n)in(a[i]);
solve();
init();
}
return 0;
}