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;
}
posted @ 2022-11-03 21:28  リン・グァン  阅读(17)  评论(0编辑  收藏  举报