NOIP模拟71
T1: 签到题
是的,我签到失败了。。。。。。
首先有一个结论就是一定存在一种方案,保证度数能被\(c\)整除的点的贡献为\(0\),同时不能被整除的点的贡献为1。
证明见这位大佬
Code
#include<bits/stdc++.h>
namespace STD
{
#define OJ
#define rr register
using std::cout;
using ll=long long;
const int N=1e6;
int n,m,k,c;
int num[N+4];
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace STD;
int main()
{
#ifdef OJ
FILE * X;
X=freopen("qiandao.in","r",stdin);
X=freopen("qiandao.out","w",stdout);
#endif
n=read(),m=read();
k=read(),c=read();
for(int i=1;i<=k;i++)
{
int x=read(),y=read()+n;
num[x]++,num[y]++;
}
ll ans=0;
for(int i=1;i<=m+n;i++)
ans+=(num[i]%c!=0);
cout<<ans<<'\n';
#ifdef OJ
fclose(stdin);
fclose(stdout);
#endif
}
T2: M 弟娃
又是一道水题,然而考场上想错了。
可以发现当\(x,y\)的\(lca\)是除他两个之外的点时,他们只会给各自子树的点贡献答案。
当\(lca\)为他们其中一个点时,假设是\(x\),那么他们只会贡献给\(S=subtree(y)\cup(tree-subtree(son_x))\)
其中\(tree\)为整棵树代表的点集,\(son_x\)为\(x\)在链\(x-y\)上的儿子。
然后线段树维护\(dfn\),支持区间加减与全局最大值即可。
Code
#include<bits/stdc++.h>
namespace Template
{
template<typename type>
inline type cmax(type x,type y){return x>y?x:y;}
#define lc id<<1
#define rc id<<1|1
template<int N>
class sgt
{
private:
struct node
{
int maxn;
int tag;
node(){maxn=tag=0;}
};
node t[N*4+4];
inline void push_down(int);
public:
void insert(int,int,int,int,int,int);
int query(){return t[1].maxn;}
};
template<int N>
inline void sgt<N>::push_down(int id)
{
if(!t[id].tag) return;
t[lc].tag+=t[id].tag;
t[rc].tag+=t[id].tag;
t[lc].maxn+=t[id].tag;
t[rc].maxn+=t[id].tag;
t[id].tag=0;
}
template<int N>
void sgt<N>::insert(int id,int l,int r,int st,int en,int val)
{
if(st<=l and r<=en)
{
t[id].maxn+=val;
t[id].tag+=val;
return ;
}
int mid=(l+r)>>1;
push_down(id);
if(st<=mid) insert(lc,l,mid,st,en,val);
if(mid<en) insert(rc,mid+1,r,st,en,val);
t[id].maxn=cmax(t[lc].maxn,t[rc].maxn);
}
#undef lc
#undef rc
};
using namespace Template;
namespace STD
{
#define OJ
#define rr register
using std::cout;
using ll=long long;
const int N=3e5;
int n,m;
int to[N*2+4],dire[N*2+4],head[N+4];
int dfn[N+4],size[N+4],fa[N+4],son[N+4],top[N+4];
int depth[N+4],reid[N+4];sgt<N> t;
inline void add(int f,int t)
{
static int num=1;
to[++num]=t;
dire[num]=head[f];
head[f]=num;
}
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-')y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
void dfs1(int x=1)
{
size[x]=1;
for(int i=head[x];i;i=dire[i])
{
if(to[i]==fa[x]) continue;
fa[to[i]]=x;
depth[to[i]]=depth[x]+1;
dfs1(to[i]);
size[x]+=size[to[i]];
if(size[to[i]]>size[son[x]]) son[x]=to[i];
}
}
void dfs2(int x=1)
{
static int num=0;
if(!x)return;
if(x==son[fa[x]]) top[x]=top[fa[x]];
else top[x]=x;
dfn[x]=++num;
reid[num]=x;
dfs2(son[x]);
for(int i=head[x];i;i=dire[i])
{
if(to[i]==fa[x] or to[i]==son[x])
continue;
dfs2(to[i]);
}
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(depth[top[x]]<depth[top[y]])
x^=y,y^=x,x^=y;
x=fa[top[x]];
}
return depth[x]<depth[y]?x:y;
}
};
using namespace STD;
int main()
{
#ifdef OJ
FILE * X;
X=freopen("magic.in","r",stdin);
X=freopen("magic.out","w",stdout);
#endif
n=read(),m=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
}
dfs1();dfs2();
while(m--)
{
int x=read(),y=read();
int lca=LCA(x,y);
if(lca!=x and lca!=y)
{
t.insert(1,1,n,dfn[x],dfn[x]+size[x]-1,1);
t.insert(1,1,n,dfn[y],dfn[y]+size[y]-1,1);
}
else
{
if(lca==x)
{
t.insert(1,1,n,dfn[y],dfn[y]+size[y]-1,1);
int last=y;
while(top[x]!=top[y])
{
last=y;
y=fa[top[y]];
}
t.insert(1,1,n,1,n,1);
if(x==y)
t.insert(1,1,n,dfn[last],dfn[last]+size[last]-1,-1);
else
t.insert(1,1,n,dfn[x]+1,dfn[x]+size[reid[dfn[x]+1]],-1);
}
else
{
t.insert(1,1,n,dfn[x],dfn[x]+size[x]-1,1);
int last=x;
while(top[x]!=top[y])
{
last=x;
x=fa[top[x]];
}
t.insert(1,1,n,1,n,1);
if(x==y)
t.insert(1,1,n,dfn[last],dfn[last]+size[last]-1,-1);
else
t.insert(1,1,n,dfn[y]+1,dfn[y]+size[reid[dfn[y]+1]],-1);
}
}
printf("%d\n",t.query());
}
#ifdef OJ
fclose(stdin);
fclose(stdout);
#endif
}
T3: 变异大老鼠
\(SPT\)(最短路树)\(DP\)。
头一次见呢。
先跑一次最短路,几下前驱,然后就可以建出最短路树了。
最短路树不一定是最小生成树。
因为最小生成树要求权值和最小,而最短路树里是所有点到定点的最短路,应该还是有区别的。
然后写个子树合并就可以了。
\(DP\)方程就是:
temp[j]=cmax(temp[j],temp[j-u]+f[son][u]);
f[x][num]=cmax(f[x][num],temp[j]*(1/son_num[x])*(1-rate[x][num-j]))
其中\(DP\)定义为在\(x\)的子树中分配\(num\)个警察能抓到老鼠的最大概率。
第一个式子是合并子树,第二个是枚举在当前点放多少个,来合并出当前子树的答案。
\(temp\)是合并子树用的容器数组。
可能是因为写法不太好,跑了2000多,别人都没过1000。
Code
#include<bits/stdc++.h>
namespace Template
{
template<typename type>
inline type cmax(type x,type y){return x>y?x:y;}
};
using namespace Template;
namespace STD
{
#define OJ
#define rr register
using std::cout;
using ll=long long;
const int M=3e4;
const int N=300;
int n,m,k;
ll dis[N+4];
bool vis[N+4];
int to[M*2+4],dire[M*2+4];
int head[N+4],w[M+4],pre[N+4];
double rate[N+4][N+4],temp[N+4];
inline void add(int f,int t)
{
static int num=1;
to[++num]=t;
dire[num]=head[f];
head[f]=num;
}
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
using std::vector;
vector<int> son[N+4];
void dijkstra()
{
memset(dis,127,sizeof(dis));
struct pack
{
int id;
ll dis;
pack(){id=dis=0;}
pack(int id_,ll dis_)
{
id=id_;
dis=dis_;
}
bool operator > (const pack x) const
{
return dis>x.dis;
}
bool operator < (const pack x) const
{
return dis<x.dis;
}
};
std::priority_queue<pack,vector<pack>,std::greater<pack> > heap;
dis[1]=0;
heap.emplace(1,0);
while(heap.size())
{
int x=heap.top().id;
heap.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=dire[i])
if(dis[to[i]]>dis[x]+w[i>>1])
{
dis[to[i]]=dis[x]+w[i>>1];
pre[to[i]]=x;
if(!vis[to[i]])
heap.emplace(to[i],dis[to[i]]);
}
}
}
double f[N+4][N+4];
void DP(int x,int num)
{
if(f[x][num]!=0.00000 and num)
return ;
if(!son[x].size())
{
f[x][num]=rate[x][num];
return;
}
for(int i=0;i<son[x].size();i++)
for(int j=0;j<=num;j++)
DP(son[x][i],j);
for(int i=0;i<=num;i++)
temp[i]=f[son[x][0]][i];
for(int i=1;i<son[x].size();i++)
for(int j=num;j;j--)
for(int u=0;u<=j;u++)
temp[j]=cmax(temp[j],temp[j-u]+f[son[x][i]][u]);
double hh=1.00/(double)son[x].size();
for(int i=0;i<=num;i++)
f[x][num]=cmax(f[x][num],temp[i]*hh*(1-rate[x][num-i])+rate[x][num-i]);
}
};
using namespace STD;
int main()
{
#ifdef OJ
FILE * X;
X=freopen("arrest.in","r",stdin);
X=freopen("arrest.out","w",stdout);
#endif
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
w[i]=read();
}
dijkstra();
for(int i=1;i<=n;i++)
son[pre[i]].emplace_back(i);
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
int ybbb=scanf("%lf",&rate[i][j]);
DP(1,k);
printf("%.6lf\n",f[1][k]);
#ifdef OJ
fclose(stdin);
fclose(stdout);
#endif
}
T4: 朝鲜时蔬
这题实际上是分类讨论写的式子。
首先是1-1,显然是\(n----(1)\)
然后是2-1,式子是
证明
考虑两个数\(a,b\),假设\(a < b\),有\(a|(a+b)\)(显然不会有\(b|(a+b)\)),那么有\(b=k * a\),枚举\(a\),考虑他的倍数,就有$\lfloor \frac{n}{i} \rfloor \(个,排除他自己,就有\){\lfloor \frac{n}{i}-1 \rfloor}$个
2-2,式子是\(C_{n}^{2}=n*(n-1)/2\)个,不解释了
3-1,式子是\(\lfloor \frac{n}{3} \rfloor\)。
证明
考虑三个数\(a,b,c\),令\(a < b < c\),首先考虑最大值是多,肯定是3,因为有例子1,2,3,那么考虑有多少个三元组\((a,b,c)\)满足有\(a|(a+b+c),b|(a+b+c),c|(a+b+c)\),首先,两边除以\((a,b,c)\),就有\(a'|(a'+b'+c'),b'|(a'+b'+c'),c'|(a'+b'+c')\)且\((a',b',c')=1\),满足这样的互质的数只有\(1,2,3\),枚举3的倍数即可。
3-2,式子是:
证明
首先假设三元组\((a,b,c)\)满足\(a < b < c\),且\((a+b)|(a+b+c) \ and \ (a+c)|(a+b+c) \ and \ (b+c)|(a+b+c)\)
实际上只会存在\((a+b)|(a+b+c)\),因为\(a< (b+c)\),\(b< (a+c)\)
那么就有\((a+b)|c\),枚举\(a+b\),考虑他的倍数与划分方式即可
考虑计算a1+a2的拆分方案数:枚举a1则可以确定唯一a2,这样“a+b形式”的会被计算2次,“a+a形式”的(如果有)会被计算1次,减掉后者的情况后除以2就能得到a1+a2的拆分方案数。
其余的见官方题解,不再抄了。
Code
#include<bits/stdc++.h>
namespace Template
{
template<typename type>
inline type cmax(type x,type y){return x>y?x:y;}
template<typename type>
inline type cmin(type x,type y){return x<y?x:y;}
};
using namespace Template;
namespace STD
{
#define OJ
#define rr register
using std::cout;
using ll=long long;
const int mod=1e9+7;
ll n,m,k;
ll read()
{
rr ll x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
ll qpow(ll base,int exp=mod-2)
{
ll ret=1LL;
while(exp)
{
if(exp&1) ret=ret*base%mod;
base=base*base%mod;
exp>>=1;
}
return ret;
}
const ll inv2=500000004;
const ll inv3=333333336;
const ll inv6=166666668;
const ll inv9=111111112;
const ll inv10=700000005;
const ll inv12=83333334;
const ll inv15=466666670;
const ll inv21=47619048;
const ll inv11=818181824;
const ll inv29=758620695;
const ll inv24=41666667;
};
using namespace STD;
int main()
{
#ifdef OJ
FILE * X;
X=freopen("vegetable.in","r",stdin);
X=freopen("vegetable.out","w",stdout);
#endif
n=read(),m=read(),k=read();
if(m==1 and k==1)
{
printf("%lld\n",n%mod);
return 0;
}
if(m==2 and k==1)
{
ll l=1,r=0;
ll ans=0;
while(r<n)
{
r=n/(n/l);
ans+=((r-l+1)%mod*((n/l)%mod)%mod-(r-l+1)%mod+mod)%mod;
if(ans>=mod) ans-=mod;
l=r+1;
}
printf("%lld\n",ans);
return 0;
}
if(m==2 and k==2)
{
n%=mod;
printf("%lld\n",n*(n-1)%mod*inv2%mod);
return 0;
}
if(m==3 and k==1)
{
n/=3LL;
printf("%lld\n",n%mod);
return 0;
}
if(m==3 and k==2)
{
auto get=
[](ll l,ll r)->ll
{
l--;
l%=mod,r%=mod;
ll x1=l*(l+1)%mod*inv2%mod;
ll x2=r*(r+1)%mod*inv2%mod;
return (x2-x1+mod)%mod;
};
//cout<<get(2,11)<<' '<<get(3,9)<<'\n';
ll ans=0ll;
ll l=1,r=0;
while(r<n)
{
r=n/(n/l);
ans+=(n/l)%mod*((get(l,r)-(r-l+1)%mod+mod)%mod)%mod;
if(ans>=mod) ans-=mod;
ans-=(n/l)%mod*((r-2LL*(l/2))/2+(l%2==0))%mod;
if(ans>=mod) ans-=mod;
if(ans<0) ans+=mod;
l=r+1;
}
printf("%lld\n",ans*inv2%mod);
return 0;
}
if(m==3 and k==3)
{
n%=mod;
printf("%lld\n",n*(n-1)%mod*(n-2)%mod*inv6%mod);
return 0;
}
if(m==4 and k==1)
{
if(n<=5) puts("1");
else
{
ll ans=0LL;
ans+=n/6;
if(ans>=mod) ans%=mod;
ans+=n/9;
if(ans>=mod) ans%=mod;
ans+=n/10;
if(ans>=mod) ans%=mod;
ans+=n/12;
if(ans>=mod) ans%=mod;
ans+=n/15;
if(ans>=mod) ans%=mod;
ans+=n/21;
if(ans>=mod) ans%=mod;
printf("%lld\n",ans);
}
return 0;
}
if(m==4 and k==2)
{
switch(n)
{
case 4LL:
puts("1");break;
case 5LL:
puts("1");break;
case 6LL:
puts("1");break;
case 7LL:
puts("3");break;
case 8LL:
puts("6");break;
case 9LL:
puts("9");break;
case 10LL:
puts("10");break;
default:
n=n/11LL+n/29LL;
printf("%lld\n",n%mod);
break;
};
return 0;
}
if(m==4 and k==3)
{
switch(n)
{
case 4LL:
puts("1");
break;
case 5LL:
puts("5");
break;
default:
auto get=
[](ll l,ll r)->ll
{
l--;
l%=mod,r%=mod;
ll x1=l*(2*l+1)%mod*(l+1)%mod*inv6%mod;
ll x2=r*(2*r+1)%mod*(r+1)%mod*inv6%mod;
return (x2-x1+mod)%mod;
};
auto get_=
[](ll l,ll r)->ll
{
l--;
l%=mod,r%=mod;
ll x1=l*(l+1)%mod*inv2%mod;
ll x2=r*(r+1)%mod*inv2%mod;
return (x2-x1+mod)%mod;
};
ll ans=0LL;
ll l=1ll,r=0ll;
while(r<n)
{
r=n/(n/l);
ll x=(n/l)%mod;
ans+=x*get(l,r)%mod;
if(ans>=mod) ans-=mod;
ans-=x*6%mod*get_(l,r)%mod;
if(ans<0) ans+=mod;
ans+=x*5%mod*(r-l+1)%mod;
if(ans>=mod) ans-=mod;
ans+=x*3%mod*(((r-2*(l/2))/2+(l%2==0))%mod)%mod;
if(ans>=mod) ans-=mod;
ans+=x*4%mod*(((r-3*(l/3))/3+(l%3==0))%mod)%mod;
if(ans>=mod) ans-=mod;
l=r+1;
}
printf("%lld\n",ans*inv12%mod);
};
return 0;
}
if(m==4 and k==4)
{
n%=mod;
printf("%lld\n",n*(n-1)%mod*(n-2)%mod*(n-3)%mod*inv24%mod);
return 0;
}
#ifdef OJ
fclose(stdin);
fclose(stdout);
#endif
}