【学习笔记】CF1054

是我降智了。

开始在想一些抽屉原理之类的东西,发现不太行。

注意到,一个位置上的 0 , 1 0,1 0,1移到左上角或右下角最多需要两步,那么我们可以将初末状态都移成 0 0 0全部在 ( 1 , 1 ) (1,1) (1,1)上, 1 1 1全部在 ( n , n ) (n,n) (n,n)上,两部分都最多需要 2 s 2s 2s步,然后就做完了。

具体做法可以自己编,总之是有一点繁琐。

复制粘贴半天才发现我把串翻转过来就行了,我是joker

二分图,但是我没看出来。

事实上,只要第一步正确,剩下的就非常容易了:我们将初始局面看成,每一个交点处连接两条横竖线段。那么只要不产生多余的交点即可,两个交点有可能合并起来让答案减少 1 1 1。将行列离散化过后相当于 n n n个点的二分图,有 n 2 n^2 n2条边,求最大独立集,然后就做完了。

没想到可以直接在残留网络上找方案,妙啊

下次研究一下残留网络的性质

不会做,只会照搬题解。

S i S_i Si表示包含点 i i i的社区的集合。如果一个社区的大小为 1 1 1,那么显然可以直接删去;否则考虑叶子节点 x x x,设其父亲节点为 y y y,那么一个连通块如果包含 x x x,显然也会包含 y y y,也就是 S x ⊆ S y S_x\subseteq S_y SxSy。这个限制看着非常亲切啊,因为假设我们知道了叶子节点 x x x,那么把所有社区中的 x x x删去,再将 x x x y y y连边就转化为一个子问题了。那么我们每次取出 ∣ S i ∣ |S_i| Si最小的作为 x x x,然后再找到父节点 y y y(其中 S x ⊆ S y S_x\subseteq S_y SxSy),然后重复上述操作就做完了。

复杂度 O ( n 2 m w ) O(\frac{n^2m}{w}) O(wn2m)

调不出来

好像要用队列写,但不知道为啥。

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define inf 0x3f3f3f3f using namespace std; int T,n,m,vis[2005],sz[2005]; bitset<2005>b[2005]; string str; vector<pair<int,int>>edges; void solve(){ edges.clear(); for(int i=1;i<n;i++){ int x=-1,y=-1; for(int j=0;j<n;j++){ if(!vis[j]&&(x==-1||b[j].count()<b[x].count())){ x=j; } } vis[x]=1; for(int j=0;j<n;j++){ if(!vis[j]&&(b[j]&b[x])==b[x]){ y=j; break; } } if(y==-1){ cout<<"NO"<<"\n"; return; } edges.pb({x,y}); for(int j=b[x]._Find_first();j!=b[x].size();j=b[x]._Find_next(j)){ if(--sz[j]==1){ b[y][j]=0; } } } cout<<"YES"<<"\n"; for(auto x:edges){ cout<<x.fi+1<<" "<<x.se+1<<"\n"; } } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>T; while(T--){ cin>>n>>m; for(int i=0;i<n;i++)b[i].reset(),vis[i]=0; for(int i=0;i<m;i++){ cin>>str,sz[i]=0; for(int j=0;j<n;j++){ if(str[j]=='1')sz[i]++; } if(sz[i]>1){ for(int j=0;j<n;j++){ if(str[j]=='1')b[j][i]=1; } } } solve(); } }

这是一道数学题。

因为 p p p是质数,根据欧拉定理,记 c k = ∑ i 2 j 3   m o d   490018 = k a i b j c_k=\sum_{i^2j^3\bmod 490018=k}a_ib_j ck=i2j3mod490018=kaibj,那么答案是 ∑ c k x k \sum c_kx^k ckxk

为什么这么拆,是因为前面很像一个卷积的形式。

根据中国剩余定理,我们只需要考虑模 2 , 491 , 499 2,491,499 2,491,499的余数,其中 491 , 499 491,499 491,499都是质数因此一定有原根, 491 491 491有原根 2 2 2 499 499 499有原根 7 7 7。因此,我们取 i 2 i^2 i2, j 3 j^3 j3的离散对数(也就是原根的指数)就能把乘法转换成加法,而模 2 2 2相当于是与运算。即求 c i , j , k = ∑ i 1 and  i 2 = 1 , j 1 + j 2 = j , k 1 + k 2 = k a i 1 , j 1 , k 1 b i 2 , j 2 , k 2 c_{i,j,k}=\sum_{i_1\text{and }i_2=1,j_1+j_2=j,k_1+k_2=k}a_{i_1,j_1,k_1}b_{i_2,j_2,k_2} ci,j,k=i1and i2=1,j1+j2=j,k1+k2=kai1,j1,k1bi2,j2,k2。这里涉及到二维 f f t fft fft的问题,可以先按行做一遍卷积,再按列做一遍卷积, i d f t idft idft也同理。不过注意 i 2 , j 3 i^2,j^3 i2,j3 491 , 499 491,499 491,499倍数的时候 没有原根,这部分需要暴力处理。

这道题有点难写。代码先咕了。

代码来了。其实是细节题。

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define inf 0x3f3f3f3f #define db double #define cpx complex<db> using namespace std; const int N=100005; const int mod=490019; const int Len=1305; const db PI=acos(-1); int n,m; ll X,A[N],B[N],a[2][Len][Len],b[2][Len][Len],ans[Len][Len],res[2][Len][Len],res2[2][Len][Len]; ll f491[500],f499[500],log491[500],log499[500]; cpx tmp[Len],c[Len][Len],d[Len][Len]; void add(ll &x,ll y){ x=(x+y)%mod; } ll fpow(ll x,ll y=mod-2){ ll z(1); for(;y;y>>=1){ if(y&1)z=z*x%mod; x=x*x%mod; } return z; } cpx omega(int n,int k,int f){ if(!f)return cpx(cos(2*PI/n*k),sin(2*PI/n*k)); return conj(cpx(cos(2*PI/n*k),sin(2*PI/n*k))); } void fft(cpx *a,int n,int f){ int k=0;while((1<<k)<n)k++; for(int i=0;i<n;i++){ int t=0; for(int j=0;j<k;j++){ if(i>>j&1){ t|=(1<<k-j-1); } } if(i<t)swap(a[i],a[t]); } for(int l=2;l<=n;l<<=1){ int m=l/2;cpx y=omega(l,1,f); for(int p=0;p!=n;p+=l){ cpx x=1; for(int i=0;i<m;i++){ cpx z=a[p+i+m]; a[p+i+m]=a[p+i]-z*x,a[p+i]+=z*x; x=x*y; } } } if(f)for(int i=0;i<n;i++)a[i]/=n; } void solve(){ int len=1024; for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ tmp[j]=c[i][j]; } fft(tmp,len,0); for(int j=0;j<len;j++){ c[i][j]=tmp[j]; } } for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ tmp[j]=c[j][i]; } fft(tmp,len,0); for(int j=0;j<len;j++){ c[j][i]=tmp[j]; } } for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ tmp[j]=d[i][j]; } fft(tmp,len,0); for(int j=0;j<len;j++){ d[i][j]=tmp[j]; } } for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ tmp[j]=d[j][i]; } fft(tmp,len,0); for(int j=0;j<len;j++){ d[j][i]=tmp[j]; } } for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ c[i][j]*=d[i][j]; } } for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ tmp[j]=c[i][j]; } fft(tmp,len,1); for(int j=0;j<len;j++){ c[i][j]=tmp[j]; } } for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ tmp[j]=c[j][i]; } fft(tmp,len,1); for(int j=0;j<len;j++){ c[j][i]=tmp[j]; } } for(int i=0;i<490;i++){ for(int j=0;j<498;j++){ ans[i][j]=0; } } for(int i=0;i<len;i++){ for(int j=0;j<len;j++){ add(ans[i%490][j%498],(ll)(c[i][j].real()+0.5)); } } } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m>>X; for(int i=0;i<n;i++)cin>>A[i]; for(int i=0;i<m;i++)cin>>B[i]; f491[0]=1; for(int i=1;i<490;i++){ f491[i]=f491[i-1]*2%491; } for(int i=0;i<490;i++){ log491[f491[i]]=i; } log491[0]=490; f499[0]=1; for(int i=1;i<498;i++){ f499[i]=f499[i-1]*7%499; } for(int i=0;i<498;i++){ log499[f499[i]]=i; } log499[0]=498; for(int i=0;i<n;i++){ ll x=(ll)i*i%(mod-1); if(x%491==0||x%499==0){ for(int j=0;j<m;j++){ ll y=(ll)j*j%(mod-1)*j%(mod-1); add(res2[x*y%2][log491[x*y%491]][log499[x*y%499]],A[i]*B[j]); } } else{ add(a[x%2][log491[x%491]][log499[x%499]],A[i]); } } for(int i=0;i<m;i++){ ll x=(ll)i*i%(mod-1)*i%(mod-1); if(x%491==0||x%499==0){ for(int j=0;j<n;j++){ ll y=(ll)j*j%(mod-1); if(y%491!=0&&y%499!=0){ add(res2[x*y%2][log491[x*y%491]][log499[x*y%499]],B[i]*A[j]); } } } else{ add(b[x%2][log491[x%491]][log499[x%499]],B[i]); } } int len=1024; for(int i=0;i<1024;i++){ for(int j=0;j<1024;j++){ c[i][j]=(a[0][i][j]+a[1][i][j])%mod; } } for(int i=0;i<1024;i++){ for(int j=0;j<1024;j++){ d[i][j]=(b[0][i][j]+b[1][i][j])%mod; } } solve(); for(int j=0;j<491;j++){ for(int k=0;k<498;k++){ add(res[0][j][k],ans[j][k]); } } for(int i=0;i<1024;i++){ for(int j=0;j<1024;j++){ c[i][j]=a[1][i][j]; } } for(int i=0;i<1024;i++){ for(int j=0;j<1024;j++){ d[i][j]=b[1][i][j]; } } solve(); for(int j=0;j<491;j++){ for(int k=0;k<498;k++){ add(res[1][j][k],ans[j][k]); add(res[0][j][k],mod-ans[j][k]); } } ll result=0; for(int i=0;i<mod-1;i++){ if(i%491==0||i%499==0){ add(result,res2[i%2][log491[i%491]][log499[i%499]]*fpow(X,i)); } else{ add(result,res[i%2][log491[i%491]][log499[i%499]]*fpow(X,i)); } } cout<<(result+mod)%mod; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529993.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示