周赛_CF758+CF760

第一题感觉就是先求gcd载检查是否正确。不过“检查”这一步骤我不是很会。

constexpr int N=1e2+2;
ll a[N];
#define fail do{puts("0");goto loop;}while(0)
int main(){
for(int t=read();t--;){
	int n=read();
	for(int i=0;i<n;++i)a[i]=read();
	vector<ll> g(a,a+2);
    for(int i = 0; i < n; i++)
    {
        g[i % 2] = __gcd(g[i % 2], a[i]);
    }  
	/*ll odd=a[1],even=a[2];
	for(int i=3;i<=n;++i){
		if(i&1)
			odd=__gcd(odd,a[i]);
		else 
			even=__gcd(even,a[i]);
	}
	//printf("#odd %lld, even %lld\n",odd,even);
	
	if(odd==even)fail;
	else if(even==1){
		for(int i=2;i<=n;i+=2)
		if(a[i]%odd==0)fail;	
	}
	else if(odd==1){
		for(int i=1;i<=n;i+=2)
		if(a[i]%even==0)fail;
	}
	printf("%lld\n",max(odd,even));
	loop:;
	*/
	bool good[2]={1,1};
	for(int i=0;i<n;++i)
		good[i%2]=good[i%2]&&(a[i]%g[(i%2)^1]>0);
	ll ans=0;
	if(good[0])ans=max(ans,g[1]);
	if(good[1])ans=max(ans,g[0]);
	printf("%lld\n",ans);
}
	return 0;
}

B

构造题都是“想出来就赢了”。
先构造一上一下的许多交叉项来获取足够数量的max和min,再把剩下的数按顺序放在一端,以确保不会造成新的max或min。

constexpr int N=1e5+2;
#define fail do{puts("-1");goto loop;}while(0)
int p[N];
inline void build(int a,int b,int n){
	for(int i=1;i<n-a-b;++i)p[i]=i;
	for(int i=1,f=1;i<=a+b+1;++i,f^=1){
		p[i+(n-a-b)-1]=f?(n-i/2):(n-a-b-1+i/2);
	}
}
int main(){
for(int t=read();t--;){
	int n=read(),a=read(),b=read();
	if(a+b>n-2||abs(a-b)>1)fail;
	if(a>=b){
		build(a,b,n);
		for(int i=1;i<=n;++i)printf("%d ",p[i]);puts("");
	}
	else {
		build(b,a,n);
		for(int i=1;i<=n;++i)printf("%d ",n+1-p[i]);puts("");
	}
	loop:;
}
	return 0;
}

C

最大的k个一定要消去,然后还需要消除次大的k个,并且小的除以小的(按顺序分别除),以减少除出1的数量。
其实就是贪心。两个区间当中谁除以谁并不会影响剩下的元素的加和。

for(int t=read(),a[102];t--;){
	int n=read(),k=read(),ans=0;
	for(int i=0;i<n;++i)a[i]=read();
	sort(a,a+n);
	for(int i=0;i<n-2*k;++i)ans+=a[i];
	for(int i=0;i<k;++i)ans+=a[n-2*k+i]/a[n-k+i];
	printf("%d\n",ans);
}

D

解线性方程组。要求所有的解为正数。

constexpr int N=4e4+2;
#define fail do{puts("NO");goto loop;}while(0)
int a[N],b[N];

int main(){ // 解线性方程组
for(int t=read();t--;){
	ll n=read(),sumb=0,d=n*(n+1)/2,suma;
	for(int i=0;i<n;++i)sumb+=b[i]=read();
	if(sumb%d)fail;
	suma=sumb/d;
	for(int i = n-1; ~i; i--) {
        ll res = (b[i] - b[(i + 1) % n] + suma);
        if(res % n != 0 || res <= 0)fail;
        a[(i + 1) % n] = res / n;
    }
    puts("YES");
    for(int i=0; i<n; i++)printf("%d ",a[i]);
    puts("");
    loop:;
}
	return 0;
}

E

tarjan缩点后,入度为0的SCC里面的点全是赢家,其余都输。
可以证明,从x开始能跑通全图 等价于 x能跑到max{a}(这个点一定可以跑通全图)

可以找到max,从他开始跑反图,能到达的点即赢家。这等价于在0号SCC里找一个点开始跑反图,反图里面所有到达的点都是赢家。DFS、BFS都行。

由于只有2种边,也可以转成双指针,0个以上返祖边覆盖的极大子区间即一个SCC。如下图。然后依然找a最大的点所在的SCC。

constexpr int N=1e5+2;
int a[N],b[N],p[N];
vector<int> ve[N];
bool vis[N];
void dfs(int u){
	vis[u]=1;//printf("#dfs %d\n",u);
	for(auto&& v:ve[u])if(!vis[v])dfs(v);
}
int main(){
for(int t=read();t--;){
	int n=read(),maxi;
	
	for(int i=0,maxa=0;i<n;++i){
		a[p[i]=i]=read();
		if(maxa<a[i])maxa=a[i],maxi=i;
		ve[i].clear();
	}
	sort(p,p+n,[](int x,int y){return a[x]<a[y];});
	for(int i=0;i<n-1;++i)ve[p[i]].push_back(p[i+1]);
	
	for(int i=0;i<n;++i)b[p[i]=i]=read();
	sort(p,p+n,[](int x,int y){return b[x]<b[y];});
	for(int i=0;i<n-1;++i)ve[p[i]].push_back(p[i+1]);
	
	memset(vis,0,n+1);
	dfs(maxi);
	for(int i=0;i<n;++i)putchar('0'^vis[i]);
	puts("");
}
	return 0;
}

F

按照题意直接构造,然后把所有答案存储在set甚至hash_table中,最后检测y是否被算出来过即可。
有效的答案的长度不会超过y的长度,这就为中间的计算确定了边界。

以上是因为数据范围有限(1e18)。数据范围大一点的话就得换高效算法了。

inline string rev(string t){
    while(t.back() == '0') t.pop_back();
    reverse(t.begin(), t.end());
    return t;
}
string bin(int x){
	return x?bin(x/2)+string(1,char(x&1^'0')):"";
}
signed main(){
	//for(int i=1;i<=64;++i)cout<<bin(i)<<endl;
	int x=read(),y=read();
	set<string>used{bin(x)};
	queue<string>q;
	for(q.push(bin(x));!q.empty();){
		string k=q.front();
		q.pop();
		if(k.size()>65)continue;
		for(int i=0; i<2; i++){
            string kk=rev(k+string(1,char('0'^i)));
            if(!used.count(kk)){
                used.insert(kk);
                q.push(kk);
            }
        }
	}
	puts(used.count(bin(y))?"YES":"NO");
	return 0;
}
posted @ 2023-03-18 16:44  全球通u1  阅读(11)  评论(0编辑  收藏  举报