Problem A
是我太菜没错了。
构造出一种可以用 (k) 个长度 (le leftlceil frac{2n}{k} ight ceil) 的链将整张图的所有点覆盖的方案,链可以有交。
既然可以有交,那么就将所有链的长度都设为最大,那么所有的链一共会覆盖 (2n) 个节点。
还有一点可以确定的是题目保证有解。
我是傻逼。
你搞一个欧拉序然后断成 (k) 段不就行了。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=2e5+5;
int n,m,k,l;bool vis[N];
struct Edge{int nxt,to;}e[M<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
int eul[N<<1],cnt_eul=0,cnt=0;;
void dfs(int u){
vis[u]=true,eul[++cnt_eul]=u;
for(int i=fir[u];i;i=e[i].nxt){
if(vis[e[i].to]) continue;
dfs(e[i].to),eul[++cnt_eul]=u;
}
}
int main(){
cin>>n>>m>>k,l=ceil(2.0*n/k);
for(int i=1;i<=m;++i){
int u,v;scanf("%d%d",&u,&v);
add(u,v,i<<1),add(v,u,i<<1|1);
}
dfs(1);
for(int i=1;i<=cnt_eul;i+=l,cnt++){
if(i+l>cnt_eul){
printf("%d ",cnt_eul-i+1);
for(int j=i;j<=cnt_eul;++j) printf("%d ",eul[j]);
printf("
");
}
else{
printf("%d ",l);
for(int j=i;j<i+l;++j) printf("%d ",eul[j]);
printf("
");
}
}
for(int i=cnt+1;i<=k;++i) printf("1 1
");
return 0;
}
Problem B
我猜测一下,如果存在一条长度为 (10^{18}) 的路径的话,就是满足存在一个导出子图,其中每一个点都有 (0) 和 (1) 两种出边,这个东西好像以前搞过。
如果不存在这么长的路径的话,就暴力去找,感觉是不会很长的?
好像猜错了。。。
依旧不会啊。。。
神 ( ext{zjj}) 提示我了矩阵乘法,但是我觉得时间复杂度不太对,是 (O(n^3log_210^{18})) 的?用 (bitset) 优化一波就是 (O(frac{n^3}{omega}log_210^{18})) 的,感觉有点假。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e2+5;
int n,m;long long res=0;
struct Matrix{int n,m;bitset<N> s[N];};
Matrix operator * (const Matrix a,const Matrix b){
Matrix res;
res.n=a.n,res.m=b.m;
for(int j=1;j<=res.m;++j){
bitset<N> B,tmp;B.reset();
for(int k=1;k<=b.n;++k) B[k]=b.s[k][j];
for(int i=1;i<=res.n;++i)
tmp=(a.s[i]&B),res.s[i][j]=tmp.any();
}
return res;
}
Matrix f[2][61];
signed main(){
cin>>n>>m;
for(int i=1;i<=m;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
f[w][0].s[u][v]=true;
}
if(f[0][0].s[1].none()) return printf("0"),0;
f[0][0].n=f[0][0].m=f[1][0].n=f[1][0].m=n;
for(int i=1;i<=60;++i){
f[0][i]=f[0][i-1]*f[1][i-1];
f[1][i]=f[1][i-1]*f[0][i-1];
}
if(f[0][60].s[1].any()) return printf("-1"),0;
Matrix tmp1,tmp2;tmp1.n=tmp1.m=n;
for(int i=1;i<=n;++i) tmp1.s[i][i]=1;
for(int i=59,tag=0;i>=0;--i){
tmp2=tmp1*f[tag][i];
// printf("%d %lld
",tag,res);
if(tmp2.s[1].any()){
tmp1=tmp2,tag^=1;
res+=(1ll<<i);
}
}
if(res>1e18) printf("-1
");
else printf("%lld
",res);
return 0;
}
Problem C
考虑有多少个集合满足其中每两个元素的异或值都是集合中的元素。
然后这个东西等价于不同的线性基的个数。
我们考虑 (dp) 求解, (f_{i,j}) 表示到第 (i) 位有 (j) 个主元的方案个数,然后考虑给上面的点附上 (0,1) 。
发现对于有主元的位,其不同的方案数只能贡献 (1) ,如果没有主元,就需要考虑 (0,1) 的奇偶性。
再用数位 (dp) 常见的套路就可以了。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M=35;
const int MOD=1e9+7;
int n,m,f[M][M][2],res=0;
int ksm(int x,int k){
int res=1;
for(;k;k>>=1,x=x*x%MOD)
if(k&1) res=res*x%MOD;
return res;
}
int fac[M],ifac[M];
int C(int n,int m){
return fac[n]*ifac[m]%MOD*ifac[n-m]%MOD;
}
int cal(int n,bool tag){
int res=0;
for(int i=0;i<=n;++i)
res+=((i&1)==tag)*C(n,i),res%=MOD;
return res;
}
signed main(){
cin>>n;
if(!n) return printf("1
"),0;
for(int x=n;x;x>>=1) m++;
fac[0]=ifac[0]=1;
for(int i=1;i<=m;++i) fac[i]=fac[i-1]*i%MOD;
for(int i=1;i<=m;++i) ifac[i]=ksm(fac[i],MOD-2);
// printf("%lld %lld
",n,m);
f[m-1][1][1]=1,f[m-1][0][0]=1;
for(int i=m-1;i>0;--i){
if(n&(1ll<<(i-1))){
for(int j=0;j<=m;++j){
f[i-1][j][1]+=f[i][j][1]*cal(j,1)%MOD,f[i-1][j][1]%=MOD;
f[i-1][j][0]+=f[i][j][1]*cal(j,0)%MOD,f[i-1][j][0]%=MOD;
f[i-1][j+1][1]+=f[i][j][1],f[i-1][j+1][1]%=MOD;
}
}
else{
for(int j=0;j<=m;++j){
f[i-1][j][1]+=f[i][j][1]*cal(j,0)%MOD,f[i-1][j][1]%=MOD;
}
}
for(int j=0;j<=m;++j){
f[i-1][j][0]+=f[i][j][0]*ksm(2,j)%MOD,f[i-1][j][0]%=MOD;
f[i-1][j+1][0]+=f[i][j][0],f[i-1][j+1][0]%=MOD;
}
}
for(int i=0;i<=m;++i) res+=f[0][i][0]+f[0][i][1],res%=MOD;
return printf("%lld
",res),0;
}
Problem D
你可以用线段树套 ( ext{set}) 来维护每一个点向下能到达的隔板位置,然后对于每一个隔板,求出其有球落在上面后可以对答案的贡献,这个可以从下向上 ( ext{dp}) ,最后统计一下答案就可以了。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
const int MOD=1e9+7;
int h,w,n;
struct Barrier{int h,l,r,lim;}a[N];
bool cmp(Barrier a,Barrier b){return a.h<b.h;}
struct Data{int id,h,lim;};
bool operator < (Data a,Data b){return a.h>b.h;}
struct Seg_Tree{
struct Node{set<Data> bag;}tr[N<<4];
void init(){
for(int i=0;i<(N<<4);++i)
tr[i].bag.insert((Data){0,0,h+1});
}
void add(int u,int l,int r,int x,int y,Data z){
if(x<=l&&r<=y) return (void)(tr[u].bag.insert(z));
int mid=(l+r)>>1;
if(x<=mid) add(u<<1,l,mid,x,y,z);
if(y>mid) add(u<<1|1,mid+1,r,x,y,z);
return ;
}
Data query(int u,int l,int r,int x,int z){
while(tr[u].bag.begin()->lim<z)
tr[u].bag.erase(tr[u].bag.begin());
// printf("%lld %lld
",l,r);
// printf("%lld %lld %lld
",tr[u].bag.begin()->id,tr[u].bag.begin()->h,tr[u].bag.begin()->lim);
if(l==r) return *tr[u].bag.begin();
int mid=(l+r)>>1;
if(x<=mid) return min(*tr[u].bag.begin(),query(u<<1,l,mid,x,z));
else return min(*tr[u].bag.begin(),query(u<<1|1,mid+1,r,x,z));
}
}t;
int f[N],res=0;
signed main(){
cin>>h>>w>>n;
for(int i=1;i<=n;++i){
scanf("%lld%lld%lld%lld",&a[i].h,&a[i].l,&a[i].r,&a[i].lim);
}
sort(a+1,a+1+n,cmp),f[0]=1,t.init();
// printf("Are you kidding me?
");
for(int i=1;i<=n;++i){
Data tmp;
if(a[i].l!=1){
tmp=t.query(1,1,w,a[i].l-1,a[i].h);
// printf("%lld %lld %lld
",tmp.id,tmp.h,tmp.lim);
f[i]+=f[tmp.id],f[i]%=MOD;
}
else{
tmp=t.query(1,1,w,a[i].r+1,a[i].h);
// printf("%lld %lld %lld
",tmp.id,tmp.h,tmp.lim);
f[i]+=f[tmp.id],f[i]%=MOD;
}
if(a[i].r!=w){
tmp=t.query(1,1,w,a[i].r+1,a[i].h);
// printf("%lld %lld %lld
",tmp.id,tmp.h,tmp.lim);
f[i]+=f[tmp.id],f[i]%=MOD;
}
else{
tmp=t.query(1,1,w,a[i].l-1,a[i].h);
// printf("%lld %lld %lld
",tmp.id,tmp.h,tmp.lim);
f[i]+=f[tmp.id],f[i]%=MOD;
}
tmp=(Data){i,a[i].h,a[i].h+a[i].lim};
t.add(1,1,w,a[i].l,a[i].r,tmp);
}
for(int i=1;i<=w;++i){
Data tmp=t.query(1,1,w,i,h+1);
res+=f[tmp.id],res%=MOD;
}
return printf("%lld
",res),0;
}
Problem E
这个数据范围感觉有点像 (O(n^3)) 的区间 ( ext{dp}) ?但是没有什么思路?
发现就是一个网络流加贪心的过程,搞一下就好了。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=205,L=1e7+5;
const int INF=1e9+7;
int n,x[N],y[N];
bitset<L> tag;vector<int> pri,bag;
int from,to,tot=0,id[N],mp[N][N];
struct Edge{int nxt,to,flow;};vector<Edge> e;int fir[N];
void add(int u,int v,int w){
e.push_back((Edge){fir[u],v,w}),fir[u]=e.size()-1;
}
int dis[N],cur[N],res=0;
queue<int> q;bool vis[N],used[N];
bool bfs(){
for(int i=1;i<=tot;++i) dis[i]=INF,cur[i]=fir[i];
dis[from]=0,vis[from]=true,q.push(from);
while(!q.empty()){
int u=q.front();vis[u]=false,q.pop();
for(int i=fir[u];i>=0;i=e[i].nxt){
int v=e[i].to;
if(!e[i].flow||dis[v]<dis[u]+1) continue;
dis[v]=dis[u]+1;if(!vis[v]) vis[v]=true,q.push(v);
}
}
return dis[to]!=INF;
}
int dfs(int u,int flow){
if(u==to) return flow;
int res=0;vis[u]=true;
for(int i=cur[u];i>=0&&flow;i=e[i].nxt){
int v=e[i].to;cur[u]=i;
if(!e[i].flow||dis[v]!=dis[u]+1||vis[v]) continue;
int tmp=dfs(v,min(flow,e[i].flow));
e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
}
return vis[u]=false,res;
}
signed main(){
for(int i=2;i<L;++i){
if(!tag[i]) pri.push_back(i);
for(int j=0;j<(int)pri.size();++j){
if(i*pri[j]>=L) break;
tag[i*pri[j]]=true;
if(i%pri[j]==0) break;
}
}
tag[0]=tag[1]=tag[2]=true;
cin>>n;
for(int i=1;i<=n;++i) scanf("%lld",&x[i]),y[i]=x[i]+1;
for(int i=1;i<=n;++i) if(y[i]==x[i+1]) y[i]=x[i+1]=0;
for(int i=1;i<=n;++i){
if(x[i]) bag.push_back(x[i]);
if(y[i]) bag.push_back(y[i]);
}
from=++tot,to=++tot;
memset(fir,-1,sizeof(fir));
for(int i=0;i<(int)bag.size();++i){
id[i]=++tot;
if(bag[i]&1) add(from,id[i],1),add(id[i],from,0);
else add(id[i],to,1),add(to,id[i],0);
}
for(int i=0;i<(int)bag.size();++i){
for(int j=i+1;j<(int)bag.size();++j){
if(!tag[abs(bag[i]-bag[j])]){
if(bag[i]&1) add(id[i],id[j],1),add(id[j],id[i],0);
else add(id[j],id[i],1),add(id[i],id[j],0);
}
}
}
while(bfs()) res+=dfs(from,INF);
// printf("%lld
",res);
int cnt[2];cnt[0]=cnt[1]=-res;
// for(int i=0;i<(int)bag.size();++i) if(!used[i]) printf("---%lld
",bag[i]);
for(int i=0;i<(int)bag.size();++i) cnt[bag[i]&1]+=(!used[i]);
res+=cnt[0]/2*2,res+=cnt[1]/2*2,cnt[0]%=2,cnt[1]%=2;
if(cnt[0]&&cnt[1]) res+=3;
printf("%lld
",res);
return 0;
}