XXII Open Cup. Grand Prix of Korea


H.Or Machine

4s

Problem

給定 \(n\) 個不超過 \(2^8\) 的數字 \(x_1~x_n\)
給定 \(l\) 個操作 \((a,b)\),表示 \(x_a = x_a | x_b\),其中 \(|\) 是按位或。
給定 \(t\) 表示進行 \(t\) 次操作,按照操作\(0\),操作\(1\),……,操作\(l-1\),操作\(0\),操作\(1\),……,操作\(l-1\),操作\(0\),操作\(1\),……,操作\(l-1\),……的順序。
求最后得到的 \(x_1~x_n\) 的值
\(1\leq n,l\leq 2^{18} , 1\leq t\leq 10^{18}\)

Solution

顯然可以把8位分開做。
即只需考慮 \(x_i \in \{0,1\}\) 的情況
可以發現 當 \(x_i\) 變成 \(1\) 后,就不可能變回 \(0\) ,於是只需求出每個數變成 \(1\) 的時間,和 \(t\) 做比較即可。
對於每個操作 從\(b\)\(a\)連邊,邊權為操作的下標。然后直接 Dijkstra 即可,當然 路徑長度不是簡單的邊權之和,具體見代碼。

Code

#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
template<typename T> inline void read(T &x){
	x=0; bool f=false; char c=getchar();
	while(!isdigit(c)){ if(c=='-') f=true; c=getchar(); }
	while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); }
	if(f) x=-x;
}
template<typename T> void print(T x){
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
const int N=1000006;
int n,l,t,x[N],y[N],dis[N],ans[N];
int to[N],w[N],nxt[N],head[N],cnt;
inline void add(int a,int b,int c){to[++cnt]=b;w[cnt]=c;nxt[cnt]=head[a];head[a]=cnt;return;}
int merg(int a,int b){
	if(a==0) return b+1;
	if((a-1)%l<b) return a+b-(a-1)%l;
	return a+b-(a-1)%l+l;
}
bool vis[N];
priority_queue<pair<int,int> > q; 
void dijkstra(){
	memset(vis,0,sizeof(vis)); 
	for(int i=1;i<=n;++i) if(dis[i]==0) q.push(make_pair(0,i));
	while(!q.empty()){
		int now=q.top().second;q.pop();
		if(vis[now]) continue; vis[now]=true;
		for(int i=head[now];i;i=nxt[i]){
			if(dis[to[i]] > merg(dis[now],w[i])){
				dis[to[i]] = merg(dis[now],w[i]);
				q.push(make_pair(-dis[to[i]],to[i]));
			}
		}
	}
	return;
}

signed main(){
	read(n);read(l);read(t);
	for(int i=0,a,b;i<l;++i) {
		read(a);read(b);
		add(b,a,i);
	}
	for(int i=1;i<=n;++i) read(x[i]);
	for(int k=1;k<256;k*=2){
		for(int i=1;i<=n;++i) dis[i]=1000000023330000018ll;
		for(int i=1;i<=n;++i) {
			y[i]=(x[i]&k);
			if(y[i]) dis[i]=0;
		}
		dijkstra();
		for(int i=1;i<=n;++i) if(dis[i]<=t) ans[i]+=k;
//		for(int i=1;i<=n;++i) cout<<dis[i]<<' '; cout<<endl;
	}
	for(int i=1;i<=n;++i) printf("%lld ",ans[i]);
	return 0;
}

J.Periodic Ruler

2s

Problem

有一把無限長的尺子,被染上了顏色,顏色用1~100的整數表示。
尺子的顏色序列有一個最小正周期。
現在已知尺子上 \(n\) 個點的顏色,求出所有的 \(t\) 滿足:無論其余點如何染色, \(t\) 都不是尺子的最小正周期。
輸出這些 \(t\) 之和,以及 \(t\) 的個數。
注意:周期不一定都是最小正周期。
\(1\leq n\leq 50,|x_i|\leq 10^9,a_i\leq 100\)

Solution

注意到\(n\)只有\(50\),但有\(100\)種顏色。
對於任意一個 \(t\),我們可以得到一個長為\(t\)的數組,把每個點的顏色按他們的坐標模\(t\)的余數對應過來。
考慮比 \(n\) 大的 \(t\)
對於任意兩個顏色不同的位置,他們的距離的因數一定滿足條件。
除此之外 一定不滿足條件。這是因為 \(t\) 一定是尺子的周期,且得到的 \(t\) 數組一定沒有被完全染色,而我們手中又有50多個沒用過的顏色,所以(我猜測)一定可以構造出一種方案使得 \(t\) 是最小正周期。
考慮不超過 \(n\)\(t\)
枚舉這些 \(t\)
假如按照模\(t\)對應過來的顏色有沖突,則滿足條件。
若沒有沖突,假如有未染色格子,則不滿足條件(同比\(n\)大的情況)。
若沒有沖突且完全染色,則\(O(t)\)枚舉所有比\(t\)小的數,檢查是不是周期。若都不是則不滿足條件,若有一個是則滿足條件。

Code

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
template<typename T> inline void read(T &x){
	x=0; bool f=false; char c=getchar();
	while(!isdigit(c)){ if(c=='-') f=true; c=getchar(); }
	while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); }
	if(f) x=-x;
}
template<typename T> void print(T x){
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
int n,x[100],a[100],cnt,sum;
map < int , bool > ans;
void solve1(int dis){
	//dis 大於 n 的 因子 
	for(int i=1;i*i<=dis;++i){
		if(dis%i==0){
			if(i>n) ans[i]=true;
			if(dis/i>n) ans[dis/i]=true;
		}
	}
	return;
}
int T[100];
bool solve2(int t){
	//若有沖突 入隊 
	//else	若未填滿  不入
	// 		若填滿 O(t^2)找有沒有更小的 --> 有則入隊  
	memset(T,0,sizeof(T));
	for(int i=1;i<=n;++i){
		if(T[x[i]%t] && T[x[i]%t]!=a[i]) return 1;
		T[x[i]%t]=a[i];
	}
	for(int i=0;i<t;++i) if(T[i]==0) return 0;
	for(int tt=1;tt<t;++tt){
		for(int i=0;i<t;++i)
			if(T[i]!=T[(i+tt)%t])
				goto nxt;
		return 1;
		nxt:;
	}
	return 0;
}
signed main(){
	read(n);
	for(int i=1;i<=n;++i) read(x[i]),read(a[i]),x[i]+=1000000010;
	for(int i=1;i<=n;++i)
		for(int j=i+1;j<=n;++j)
			if(a[i]!=a[j]){
				solve1(abs(x[i]-x[j]));
			}
	for(int t=1;t<=n;++t){
		if(solve2(t)) ans[t]=true;
	} 
	for(auto it : ans) if(it.second) ++cnt,sum+=it.first/*,print(it.first)*/;
	printf("%lld %lld",cnt,sum);
	return 0;
}

A.Automatic Sprayer 2

2s

Problem

兩個 \(n*n\) 的矩陣 \(A,E\)。滿足:\(E_{i,j}=\sum_{x=1}^n\sum_{y=1}^n dis(i,j,x,y)*A_{x,y}\),where \(dis(i,j,x,y)=|i-x|+|j-y|\)
給出 \(n,E\) ,求 \(A\)

Solution

\(E\)的任意一行二階差分得到矩陣\(A\)\(2~n-1\)列每列的和\(sum1[2~n-1]\)
\(E\)的任意一列二階差分得到矩陣\(A\)\(2~n-1\)行每行的和\(sum2[2~n-1]\)
對於\(E\)的四個角,以左上角為例。
\(E_{1,1}=\sum_{i=1}^n (i-1)*sum1[i] + \sum_{i=1}^n (i-1)*sum2[i]=(n-1)*(sum1[n]+sum2[n])+\sum_{i=1}^{n-1} (i-1)*sum1[i] + \sum_{i=1}^{n-1} (i-1)*sum2[i]\)
於是可以求出\(sum1[n]+sum2[n]\)。同理得到\(sum1[1]+sum2[1]\),\(sum1[1]+sum2[n]\)。再結合 \(\sum_{i=1}^n sum1[i] = \sum_{i=1}^n sum2[i]\)。即可解出 \(sum1[1],sum1[n],sum2[1],sum2[n]\)
這樣就得到了所有的 \(sum1[1~n],sum2[1~n]\)
下面只要構造任何一個滿足上述條件的 \(A\) 即可。
構造方法:枚舉每個格子\((i,j)\)\(A_{i,j}=min(sum1[j],sum2[i])\)\(sum1[j]-=A_{i,j},sum2[i]-=A_{i,j}\)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM