2021.10.7考试总结[NOIP模拟71]
信心赛,但炸了。T3SB错直接炸飞,T4可以硬算的组合数非要分段打表求阶乘。。T2也因为一个细节浪费了大量时间。。
会做难题很好,但首先还是要先把能拿的分都拿到。
T1 签到题
结论:总可以做到对每个点连的边平均分配颜色。感性理解貌似不难,据题解说可以网络流证明。
总之很玄。
UPD:证明好像能用\(vizing\)定理?
放两个链接
\(code:\)
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
inline int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
inline void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void ckmax(int& x,int y){ x=x<y?y:x; }
inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=1000010;
int n,m,k,c,ans,in[NN][2],u[NN],v[NN];
signed main(){
freopen("qiandao.in","r",stdin);
freopen("qiandao.out","w",stdout);
n=read(); m=read(); k=read(); c=read();
for(int i=1;i<=k;i++){
u[i]=read(); v[i]=read();
++in[u[i]][0];
++in[v[i]][1];
}
for(int i=1;i<=n;i++) ans+=(bool)(in[i][0]%c);
for(int i=1;i<=m;i++) ans+=(bool)(in[i][1]%c);
write(ans,'\n');
return 0;
}
T2 M弟娃
考虑每对幻境会对哪些点产生贡献。
如果两点相同,则对所有点有贡献。
如果这个点对具有祖先关系,那么它会对除祖先节点向深度较深节点方向的儿子的子树外其它点产生贡献,也会对深度较深节点的子树产生贡献。
如果没有,那么会对两个点的子树产生贡献。
线段树区间加,全局查最大值即可。
\(code:\)
T2
%: pragma GCC optimize(12)
#include<bits/stdc++.h>
using namespace std;
namespace IO{
int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
int max(int x,int y){ return x<y?y:x; }
int min(int x,int y){ return x<y?x:y; }
void swap(int& x,int& y){ x^=y^=x^=y; }
void ckmax(int& x,int y){ x=x<y?y:x; }
void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=300010;
int n,m,x,y,lca,idx,to[NN<<1],nex[NN<<1],head[NN<<1];
void add(int a,int b){
to[++idx]=b; nex[idx]=head[a]; head[a]=idx;
to[++idx]=a; nex[idx]=head[b]; head[b]=idx;
}
namespace segment_tree{
#define ld rt<<1
#define rd (rt<<1)|1
int mx[NN<<2],tag[NN<<2];
void pushup(int rt){ mx[rt]=max(mx[ld],mx[rd]); }
void pushdown(int rt){
mx[ld]+=tag[rt]; mx[rd]+=tag[rt];
tag[ld]+=tag[rt]; tag[rd]+=tag[rt];
tag[rt]=0;
}
void insert(int rt,int l,int r,int opl,int opr){
if(l>=opl&&r<=opr){
++mx[rt]; ++tag[rt];
return;
}
pushdown(rt);
int mid=l+r>>1;
if(opl<=mid) insert(ld,l,mid,opl,opr);
if(opr>mid) insert(rd,mid+1,r,opl,opr);
pushup(rt);
}
} using namespace segment_tree;
namespace tree_devide{
int cnt,fa[NN],id[NN],dfn[NN],top[NN],dep[NN],siz[NN],son[NN];
void dfs1(int s,int f){
fa[s]=f; dep[s]=dep[f]+1; siz[s]=1;
for(int i=head[s];i;i=nex[i]){
int v=to[i];
if(v==f) continue;
dfs1(v,s);
siz[s]+=siz[v];
if(siz[v]>siz[son[s]]) son[s]=v;
}
}
void dfs2(int s,int t){
top[s]=t; dfn[s]=++cnt; id[cnt]=s;
if(!son[s]) return;
dfs2(son[s],t);
for(int i=head[s];i;i=nex[i]){
int v=to[i];
if(v!=fa[s]&&v!=son[s]) dfs2(v,v);
}
}
int LCA(int u,int v){
while(top[u]!=top[v])
if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
else v=fa[top[v]];
return dep[u]<dep[v]?u:v;
}
int getpos(int u,int v){
if(dep[u]>dep[v]) swap(u,v);
while(top[u]!=top[v]){
if(fa[top[v]]==u) return top[v];
v=fa[top[v]];
}
int l=dfn[u],r=dfn[v],res=r;
while(l<=r){
int mid=l+r>>1;
if(dep[id[mid]]<=dep[u]) l=mid+1;
else r=mid-1, res=mid;
}
return id[res];
}
} using namespace tree_devide;
signed main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
n=read(); m=read();
for(int i=1;i<n;i++) add(read(),read());
dfs1(1,0); dfs2(1,1);
while(m--){
x=read(); y=read();
lca=LCA(x,y);
if(x==y) insert(1,1,n,1,n);
else if(lca==x){
int pos=getpos(x,y);
insert(1,1,n,1,dfn[pos]-1);
insert(1,1,n,dfn[pos]+siz[pos],n);
insert(1,1,n,dfn[y],dfn[y]+siz[y]-1);
} else if(lca==y){
int pos=getpos(x,y);
insert(1,1,n,1,dfn[pos]-1);
insert(1,1,n,dfn[pos]+siz[pos],n);
insert(1,1,n,dfn[x],dfn[x]+siz[x]-1);
} else{
insert(1,1,n,dfn[x],dfn[x]+siz[x]-1);
insert(1,1,n,dfn[y],dfn[y]+siz[y]-1);
}
write(mx[1],'\n');
}
return 0;
}
T3 变异大老鼠
每个点最短路是唯一的,相当于每个点只能从一个点转移。那么问题就转化为了树上问题。
做树上背包,每次先合并子树,之后枚举当前点选几个算概率即可。
\(code:\)
T3
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef double DB;
namespace IO{
inline int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
inline void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void ckmax(int& x,int y){ x=x<y?y:x; }
inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=310,MM=60010;
int n,m,k;
vector<int>e[NN];
DB ans,g[NN],pw[NN][NN],f[NN][NN];
namespace shortest_path{
typedef pair<int,int> PII;
#define x first
#define y second
#define mp make_pair
int idx,nex[MM],to[MM],head[NN],w[MM];
int pre[NN],dis[NN];
bool vis[NN];
priority_queue<PII,vector<PII>,greater<PII>>q;
void add(int a,int b,int c){
to[++idx]=b; nex[idx]=head[a]; head[a]=idx; w[idx]=c;
to[++idx]=a; nex[idx]=head[b]; head[b]=idx; w[idx]=c;
}
void dijstra(){
memset(dis,0x3f,sizeof(dis));
dis[1]=0; q.push(mp(0,1));
while(!q.empty()){
int x=q.top().y,y=q.top().x;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=nex[i]){
int v=to[i];
if(dis[v]<=y+w[i]) continue;
dis[v]=y+w[i];
pre[v]=x;
q.push(mp(dis[v],v));
}
}
for(int i=2;i<=n;i++)
e[pre[i]].push_back(i);
}
} using namespace shortest_path;
void dfs(int pos){
if(!e[pos].size()){
for(int i=1;i<=k;i++) f[pos][i]=pw[pos][i];
return;
}
for(int v:e[pos]) dfs(v);
memset(g,0,sizeof(g));
for(int v:e[pos])
for(int j=k;~j;j--)
for(int u=1;u<=j;u++)
g[j]=max(g[j],g[j-u]+f[v][u]);
for(int i=1;i<=k;i++) g[i]/=1.0*e[pos].size();
for(int i=1;i<=k;i++){
f[pos][i]=g[i];
for(int j=1;j<=i;j++)
f[pos][i]=max(f[pos][i],pw[pos][j]+(1-pw[pos][j])*g[i-j]);
}
}
signed main(){
freopen("arrest.in","r",stdin);
freopen("arrest.out","w",stdout);
n=read(); m=read(); k=read();
for(int a,b,c,i=1;i<=m;i++)
a=read(),b=read(),c=read(),add(a,b,c);
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
scanf("%lf",&pw[i][j]);
dijstra(); dfs(1);
printf("%.6lf\n",f[1][k]);
return 0;
}
T4 朝鲜时蔬
题面究极套娃。。
大意:求有多少个大小为\(m\)的集合\(S\),满足\(\forall a \in S,a\leq n \wedge a\in \mathbb{N}^*\),存在大小为\(k\)的子集\(T\),使\((\sum_{a\in T}a)|(\sum_{b\in S}b)\),且\(|T|\)在所有\(S\)的所有\(T\)中最大,并且当前\(S\)存在\(T\)的数量为所有\(S\)中最多。(还是绕得离谱
一看题目范围就想到了原来那道分类讨论\(24\)种情况的题,题解果然让人不失所望。
分类讨论十种情况,先推出\(|T|\)的最大值,然后或\(\Theta(1)\)算或\(\Theta(\sqrt n)\)整除分块做。具体看题解吧。五页分类讨论真不是我能独立整出来的。。
\(code:\)
T4
#include<bits/stdc++.h>
#define int long long
using namespace std;
// ????????????????????????????????????
namespace IO{
inline int read(){
char ch=getchar(); int x=0,f=1;
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
inline void write(int x,char sp){
char ch[20]; int len=0;
if(x<0){ putchar('-'); x=~x+1; }
do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
}
inline int max(int x,int y){ return x<y?y:x; }
inline int min(int x,int y){ return x<y?x:y; }
inline void swap(int& x,int& y){ x^=y^=x^=y; }
inline void ckmax(int& x,int y){ x=x<y?y:x; }
inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int p=1e9+7;
int n,m,k,l,r,ans;
int qpow(int a,int b){
int res=1; a%=p;
for(;b;b>>=1){
if(b&1) res=res*a%p;
a=a*a%p;
}
return res;
}
namespace devide{
void task11(){
ans=1;
for(int i=0;i<k;i++) (ans*=(n-i)%p)%=p;
for(int i=2;i<=k;i++) (ans*=qpow(i,p-2))%=p;
}
void task21(){
l=1;
while(l<=n){
r=n/(n/l);
(ans+=(r-l+1)%p*(n/l-1)%p)%=p;
l=r+1;
}
}
void task22(){
ans=1;
for(int i=0;i<k;i++) (ans*=(n-i)%p)%=p;
for(int i=2;i<=k;i++) (ans*=qpow(i,p-2))%=p;
}
void task31(){
ans=n/3%p;
}
void task32(){
l=1; int inv2=qpow(2,p-2),res,num;
while(l<=n){
r=n/(n/l);
num=r/2-(l-1)/2;
res=((l+r-2)%p*((r-l+1)%p)%p*inv2%p-num%p)*inv2%p;
if(res<0) res+=p;
(ans+=(n/l)%p*res)%=p;
l=r+1;
}
}
void task33(){
ans=1;
for(int i=0;i<k;i++) (ans*=(n-i)%p)%=p;
for(int i=2;i<=k;i++) (ans*=qpow(i,p-2))%=p;
}
void task41(){
if(n==4||n==5) ans=1;
else ans=(n/6%p+n/9%p+n/10%p+n/12%p+n/15%p+n/21%p)%p;
}
void task42(){
if(n<7) ans=1;
if(n==7) ans=3;
if(n==8) ans=6;
if(n==9) ans=9;
if(n==10) ans=10;
if(n>10) ans=(n/11%p+n/29%p)%p;
}
void task43(){
if(n==4) ans=1;
if(n==5) ans=5;
if(n<6) return;
l=1;
int L,tmp,res,num2,num3,inv2=qpow(2,p-2),inv6=qpow(6,p-2),inv12=inv2*inv6%p;
while(l<=n){
r=n/(n/l); L=l-1;
num2=3*(r/2-(l-1)/2)%p;
num3=4*(r/3-(l-1)/3)%p;
tmp=(p+(r%p*((r+1)%p)%p*((2*r+1)%p)%p)-(L%p*((L+1)%p)%p*((2*L+1)%p)%p))*inv6%p;
res=(p+tmp+(r-l+1)*5%p-6*((l+r)%p)*((r-l+1)%p)%p*inv2%p+num2+num3)%p*inv12%p;
(ans+=(n/l)%p*res)%=p;
l=r+1;
}
}
void task44(){
ans=1;
for(int i=0;i<k;i++) (ans*=(n-i)%p)%=p;
for(int i=2;i<=k;i++) (ans*=qpow(i,p-2))%=p;
}
void work(){
if(m==1&&k==1) task11();
if(m==2&&k==1) task21();
if(m==2&&k==2) task22();
if(m==3&&k==1) task31();
if(m==3&&k==2) task32();
if(m==3&&k==3) task33();
if(m==4&&k==1) task41();
if(m==4&&k==2) task42();
if(m==4&&k==3) task43();
if(m==4&&k==4) task44();
}
} using namespace devide;
signed main(){
freopen("vegetable.in","r",stdin);
freopen("vegetable.out","w",stdout);
n=read(); m=read(); k=read();
work();
write(ans,'\n');
return 0;
}