题面(加密)

不得不说这次的题除了引起单身汪极度不适之外还是出的很有水平的……

A.

很好的dp题

模型非常简单,如果数据范围足够友好的话就是一道dp入门题

30%:

我们可以设$dp[i][j]$为到第i天一共喂食给出了j块饼干的方案数

易得转移方程:$dp[i][j+k]=\sum \limits_{k=0}^{min(m-1,n-j)}{dp[i-1][j]}$,i枚举天数,j枚举已给出数量,k枚举下一步给出数量

$\sum \limits_{i=1}^{n}{dp[i][n]}$即为答案

但是i是要枚举到d的,我们的二维数组显然开不了这么大,所以我们可以得到30分的好成绩(滑稽

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=;
const ll mod=;
int n,m;
ll dp[N][N],D;
void work()
{
if(1LL*n>=1LL*m*D||m<=)
{
puts("");
return ;
}
memset(dp,,sizeof(dp));
for(int i=;i<=min(m-,n);i++)
dp[][i]=;
for(int i=;i<=D;i++)
for(int j=;j<min(1LL*n,D*m);j++)
for(int k=;k<=min(m-,n-j);k++)
(dp[i][j+k]+=dp[i-][j])%=mod;
ll ans=;
for(int i=;i<=D;i++)
(ans+=dp[i][n])%=mod;
cout<<ans%mod<<endl;
}
int main()
{
while()
{
scanf("%d%lld%d",&n,&D,&m);
if(n==&&D==&&m==)break;
work();
}
return ;
}

(另外,冲着30分去就按着30分范围打,不要梦想把数组开到极限还能多得分……事实上博主的30分就因为数组开太大 炸了内存 没了)

100%:

d的范围十分大,但我们不难发现真正给出饼干的天数最多只有n

所以可以把上面那个状态数组的定义稍作更改:真正给出饼干天数为i,给出饼干数量为j时的方案数

另外,为了进一步优化复杂度,使用前缀和优化,我们需要对枚举变量的意义作改动,让等号左侧为$dp[i][j]$

$dp[i][j]=\sum \limits_{k=j-m+1}^{j-1}{dp[i-1][k]}$

在统计结果时,对于每个$dp[i][n]$,是从d天中选任意i天给出饼干,所以还要乘上相应组合数

即:$ans=\sum {dp[i][n]*C_d^i}$

组合数直接根据相邻两项关系递推算即可 记得不能直接除 要乘逆元

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=;
typedef long long ll;
const ll mod=;
ll dp[N][N],n,d,m,fac[N],C[N],sum[N];
ll qpow(ll a,ll b)
{
ll res=;
while(b)
{
if(b&)res=res*a%mod;
a=a*a%mod;
b>>=;
}
//if(res<0)cout<<"Jackpot!"<<endl;
return res;
}
ll inv(ll x)
{
return qpow(x,mod-);
}
void work()
{
if(n>=m*d||m<=)
{
puts("");
return ;
}
memset(dp,,sizeof(dp));
memset(sum,,sizeof(sum));
C[]=;
for(int i=;i<=min(n,d);i++)
{
C[i]=(C[i-]*(1LL*(d-i+)%mod))%mod*inv(i)%mod;
/* if(C[i]<0)while(1);
cout<<"***"<<C[i]<<endl;*/
}
for(int i=;i<m;i++)
dp[][i]=,(sum[i]=sum[i-]+dp[][i])%=mod;
for(int i=m;i<=n;i++)
sum[i]=sum[i-];
for(int i=;i<=n;i++)
{
for(int j=;j<=n;j++)
dp[i][j]=(sum[j-]-sum[max(0LL,j-m)]+mod)%mod;
// sum[0]=0;
for(int j=;j<=n;j++)
(sum[j]=sum[j-]+dp[i][j])%=mod;
} ll ans=;
for(int i=;i<=min(d,n);i++)
(ans+=dp[i][n]*C[i]%mod)%=mod;
cout<<ans<<endl; }
int main()
{
while()
{
scanf("%lld%lld%lld",&n,&d,&m);
if(!n&&!d&&!m)break;
work();
}
return ;
}

B.

数据很水,水到直接把这道题变成了最短路的板子……

枚举与1节点相连的点,对于每个点,把1和他们之间的边临时断开后跑最短路

用$dis[1]+len[i]$更新ans即可

正解似乎是什么二进制分组blablabla  我也不会啊

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define pa pair<int,int>
using namespace std;
const int N=,M=;
int n,m,T;
int to[M<<],dis[N],vis[N],nxt[M<<],tot,head[N],del[M<<],len[M<<];
int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
return x*f;
}
void add(int x,int y,int z)
{
to[++tot]=y;
len[tot]=z;
nxt[tot]=head[x];
head[x]=tot;
}
void dj(int s)
{
memset(dis,0x3f,sizeof(dis));
memset(vis,,sizeof(vis));
priority_queue<pa> q;
dis[s]=;//vis[s]=1;
q.push(make_pair(,s));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(vis[x])continue;
vis[x]=;
for(int i=head[x];i;i=nxt[i])
{
if(del[i])continue;
int y=to[i];
if(dis[y]>dis[x]+len[i])
{
dis[y]=dis[x]+len[i];
q.push(make_pair(-dis[y],y));
}
}
}
/*for(int i=1;i<=n;i++)
cout<<dis[i]<<' ';
puts(" ");*/
}
void ini()
{
for(int i=;i<=n;i++)head[i]=;
for(int i=;i<=m*;i++)to[i]=nxt[i]=del[i]=len[i]=;
tot=;
}
void work()
{
n=read();m=read();
ini();
for(int i=;i<=m;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
int ans=0x3f3f3f3f;;
for(int i=head[];i;i=nxt[i])
{
int y=to[i];
del[i]=del[i^]=;
dj(y);
del[i]=del[i^]=;
ans=min(ans,dis[]+len[i]);
}
cout<<(ans==0x3f3f3f3f?-:ans)<<endl;
return ;
}
int main()
{
T=read();
while(T--)work();
return ;
}

(如果要使用$i\ xor\ 1$查询反边这种操作的话,链式前向星的tot初值要设成1!!!一时nc又少30!!)

C.

鬼能想到正解的神题……

首先我们考虑对这个模型进行转化。我们把2n个数字看作节点,把每张卡牌看作
是连接两个点的有向边。
于是我们的问题可以等价转化为这一问题:把图中的某些边反向使得任何一个节点
至多被一条边指向。
我们很容易想到整张图可能会被分为若干个联通块。
首先我们考虑联通块构成了树的情况。我们发现我们可以直接dp
然后我们考虑联通块构成了基环外向树的情况。我们发现只要确定环上的边的指向,
后面的外向树部分的指向就可以确定了。对于环上面的指向问题,我们发现一共只有两种
指向的可能,枚举即可。
随后我们考虑更复杂的情况,n个点,n+1条边的情况,因为我们知道每一条边肯定
会至少指向一个点,所以由抽屉原理可以一定存在一个点被指向多次,所以一定不可行。
所以,我们可以发现我们需要处理的即一个基环外向树和树的森林。
而联通块之间互不影响,分别处理即可。

树的情况需要换根进行dp。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=;
const ll mod=;
int T,n,to[N<<],nxt[N<<],head[N],tot=;
int vis[N],v[N],f[N],g[N],edge,node;
int cire,pos,poss;
vector<int> sor;
int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
return x*f;
}
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x)
{
vis[x]=;node++;
for(int i=head[x];i;i=nxt[i])
{
edge++;
if(vis[to[i]])continue;
dfs(to[i]);
}
}
void dp1(int x,int fa)
{
v[x]=;
for(int i=head[x];i;i=nxt[i])
{
if(to[i]==fa)continue;
if(!v[to[i]])
{
dp1(to[i],x);
f[x]+=f[to[i]]+(i&);
}
else pos=x,poss=to[i],cire=i;
}
}
void dp2(int x,int fa)
{
sor.push_back(g[x]);
for(int i=head[x];i;i=nxt[i])
{
if(to[i]==fa)continue;
if(i==cire||i==(cire^))
continue;
int contr=((i&)?-:);
g[to[i]]=g[x]+contr;
dp2(to[i],x);
}
}
void ini()
{
for(int i=;i<=n*;i++)
vis[i]=v[i]=f[i]=g[i]=head[i]=;
for(int i=;i<=n*;i++)
nxt[i]=to[i]=;
tot=;edge=node=;
}
void work()
{
n=read();ini();
for(int i=;i<=n;i++)
{
int x=read(),y=read();
add(y,x);add(x,y);
}
/*for(int i=2;i<=tot;i++)
cout<<'!'<<to[i]<<' '<<nxt[i]<<' '<<endl;*/
for(int i=;i<=n;i++)
if(!vis[i])
{
node=edge=;
dfs(i);
if((edge>>)>node)
{
puts("-1 -1");
return ;
}
}
int ans1=,num=;ll ans2=;
for(int i=;i<=(n<<);i++)
{
if(v[i])continue;
sor.clear();
num=cire=pos=poss=;
dp1(i,);g[i]=f[i];
dp2(i,);
if(!cire)
{
sort(sor.begin(),sor.end());//puts("QAQ");
for(int j=;j<sor.size();j++)
{
//cout<<'!'<<sor[j]<<endl;
if(sor[j]!=sor[])break;
num++;
}
ans1+=sor[];//cout<<"***"<<ans1<<endl;
}
else
{
cire=(cire&);//puts("QwQ");
if(g[pos]+(cire^)==g[poss]+cire)num=;
else num=;
ans1+=min(g[pos]+(cire^),g[poss]+cire);//cout<<"###"<<ans1<<endl;
}
(ans2*=1LL*num%mod)%=mod;
}
/*puts("---"); for(int i=1;i<=n*2;i++)
cout<<f[i]<<' '<<g[i]<<endl;puts("---");*/
cout<<ans1<<' '<<ans2<<endl;
}
int main()
{
T=read();
while(T--)work();
return ;
}

最新文章

  1. 【总结】C# 设置委托的机理和简要步骤
  2. ubuntu 安装zabbix_agent端
  3. Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 2) B. Bear and Three Musketeers 枚举
  4. 硬盘参数之TLER
  5. DFT设计绪论
  6. 重写 button 的创建方法
  7. GC: 垃圾回收算法
  8. 解决Navicat Error: Missing required libmysql_d.dll
  9. dojo 学习笔记
  10. 快速了解Hibernate的使用
  11. 剑指offer-二叉树的下一个节点
  12. vue独立构建和运行构建
  13. Python学习计划
  14. localstorage 更新监测 storage事件
  15. Android 安装 卸载 更新 程序
  16. android 很多应用中用到的 listView + viewPager
  17. 1.1 C++布尔类型(bool)
  18. 第二章:走进shell
  19. 在linux下监控文件是否被删除或创建的命令
  20. boost编译很慢的解决方法

热门文章

  1. 使用node-static运行vue打包文件dist
  2. Adobe 2019 全家桶 Win 版
  3. angular-cli项目报Error encountered resolving symbol values statically. Function calls are not supported.错误的处理。
  4. Python字典操作及课后练习
  5. 埋在MySQL数据库应用中的17个关键问题
  6. Celery &#39;Getting Started&#39; not able to retrieve results; always pending
  7. 启动项目时,Mysql的连接问题
  8. MyBatis原理,Spring、SpringBoot整合MyBatis
  9. Spring Cloud配置中心客户端读取配置
  10. 注解深入浅出之Retrofit中的注解(三)