给定 \(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}\)
显然可以把8位分开做。
即只需考虑 \(x_i \in \{0,1\}\) 的情况
可以发现 当 \(x_i\) 变成 \(1\) 后,就不可能变回 \(0\) ,于是只需求出每个数变成 \(1\) 的时间,和 \(t\) 做比较即可。
对于每个操作 从\(b\)向\(a\)连边,边权为操作的下标。然后直接 Dijkstra 即可,当然 路径长度不是简单的边权之和,具体见代码。
#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; }
有一把无限长的尺子,被染上了颜色,颜色用1~100的整数表示。
尺子的颜色序列有一个最小正周期。
现在已知尺子上 \(n\) 个点的颜色,求出所有的 \(t\) 满足:无论其余点如何染色, \(t\) 都不是尺子的最小正周期。
输出这些 \(t\) 之和,以及 \(t\) 的个数。
注意:周期不一定都是最小正周期。
\(1\leq n\leq 50,|x_i|\leq 10^9,a_i\leq 100\)
注意到\(n\)只有\(50\),但有\(100\)种颜色。
对于任意一个 \(t\),我们可以得到一个长为\(t\)的数组,把每个点的颜色按他们的坐标模\(t\)的余数对应过来。
考虑比 \(n\) 大的 \(t\)
对于任意两个颜色不同的位置,他们的距离的因数一定满足条件。
除此之外 一定不满足条件。这是因为 \(t\) 一定是尺子的周期,且得到的 \(t\) 数组一定没有被完全染色,而我们手中又有50多个没用过的颜色,所以(我猜测)一定可以构造出一种方案使得 \(t\) 是最小正周期。
考虑不超过 \(n\) 的 \(t\)
枚举这些 \(t\),
假如按照模\(t\)对应过来的颜色有冲突,则满足条件。
若没有冲突,假如有未染色格子,则不满足条件(同比\(n\)大的情况)。
若没有冲突且完全染色,则\(O(t)\)枚举所有比\(t\)小的数,检查是不是周期。若都不是则不满足条件,若有一个是则满足条件。
#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; }
两个 \(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\)。
对\(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}\)。