T1:工业题

基本思路

  这题有一个重要的小转化:

我们将原来的函数看作一个矩阵,\(f(i,j-1)*a\)相当于从\(j-1\)向右走一步并贡献a,\(f(i-1,j)*b\)相当于从\(i-1\)向下走一步并贡献b

  那么问题就转化成了求从第\(0\)行与第\(0\)列的所有点走到点\((m,n)\)的所有方案数的总贡献

  在一个点,对于他之前的点的所有走法,他都有可能向下或右走并带来贡献,所以是统计所有方案数。

  易知从点\((i,j)\)到点\((m,n)\)的走的步数是\(m-i+n-j)\),又由于在每一个点都有向下或向右走的可能,我们可以只关注向下(或向右),看每一步向下(或向右)走发生在哪个点,那么方案数就是\(C_{n+m-i}^{n-i}\)(从第0列出发)及\(C_{n+m-i}^{m-i}\)(从第0行出发)

  你这么打了,然后你WA了。。。。。。

  为什么?

  注意:这里的C考虑的是决策点,但是,第0行(列)的点一定会向下(右)走,是不会有决策的,因而正确的式子是:

\[C_{n+m-i-1}^{n-i}andC_{n+m-i-1}^{m-i}
\]

  上代码:

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
const int SIZE=3e5+4;
const int mod=998244353;
int n,m;
ll a,b,ans;
ll jc[SIZE<<1],inv[SIZE<<1];
ll fn[SIZE],fm[SIZE];
ll b_,a_;
ll qpow(ll base,ll exp)
{
rr ll ret=1ll;
while(exp)
{
if(exp&1) ret=ret*base%mod;
base=base*base%mod;
exp>>=1ll;
}
return ret;
}
inline ll C(ll x,ll y){return jc[x]*inv[y]%mod*inv[x-y]%mod;}
ll read1()
{
rr ll x_read=0ll,y_read=1ll;
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*10+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
int read2()
{
rr int x_read=0ll,y_read=1ll;
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*10+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace STD;
int main()
{
n=read2(),m=read2(),a=read1(),b=read1();
a%=mod,b%=mod;
a_=qpow(a,m);
b_=qpow(b,n);
//由于后面a^m与b^n会大量用到,所以预处理
for(rr int i=1;i<=n;i++){fn[i]=read1();fn[i]%=mod;}
for(rr int i=1;i<=m;i++){fm[i]=read1();fm[i]%=mod;}
jc[0]=inv[0]=1ll;
for(rr int i=1;i<=n+m;i++){jc[i]=1ll*i*jc[i-1]%mod;inv[i]=qpow(jc[i],mod-2);}
for(rr int i=1;i<=n;i++)
ans=(ans+fn[i]*C(n+m-i-1,m-1)%mod*qpow(b,n-i)%mod*a_%mod)%mod;
for(rr int i=1;i<=m;i++)
ans=(ans+fm[i]*C(n+m-i-1,n-1)%mod*b_%mod*qpow(a,m-i)%mod)%mod;
int x=printf("%lld",ans);
}

T2:

基本思路:

  考场上想的普通DP,不对。

  实际上是树形DP

  实际上可以将Y方点看作边,然后将X方点看作点,\(n\)个点\(n\)条边,很明显,去掉一条边后就是树,我们的任务就是求能将所有的边都覆盖的最小代价,一条边被覆盖是指它的两个端点至少有一个被选。

  我们可以判出环来然后删去环里的任一条边,然后以删掉的边的端点跑DP即可

  很明显树形DP,DP式就不写在这了,自己推吧,推不出来就自己上代码里找吧。

  状态数组\(c[i][j](j=0,1)\)表示i被选(j=1)或不被选(j=0)是以他为根的子树的最小代价。

  注意被删掉的边也要被覆盖,所以要在两个端点被选的情况里取min

  代码:

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
#define pp pair<int,int>
#define fi first
#define se second
#define min(x,y) (x<y?x:y)
#define inf LONG_LONG_MAX
#define IT set<int>::iterator
const int SIZE=1e6+4;
int n,root;
ll ans=inf,a,b;
set<int> to[SIZE];
ll val[SIZE];
ll c[SIZE][2];
bool vis[SIZE];
pp edge;
void dfs(int x,int f)
{
static bool ret=0;
vis[x]=1;
for(IT it=to[x].begin();it!=to[x].end();it++)
{
if(*it==f) continue;
if(vis[*it]){edge.fi=x,edge.se=*it;ret=1;return;}
dfs(*it,x);
if(ret) return;
}
}
void DP(int now,int f)
{
//儿子与父亲必须至少有一个选
for(IT it=to[now].begin();it!=to[now].end();it++)
{
if(*it==f) continue;
DP(*it,now);
c[now][1]+=min(c[*it][0],c[*it][1]);
c[now][0]+=c[*it][1];
//鄙视你的智商,连我都推出来的式子你还推不出来过来看。。。。
}
c[now][1]+=val[now];
}
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*10)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace STD;
int main()
{
n=read(),a=read(),b=read();
for(rr int i=1;i<=n;i++)
{
int x1=read(),x2=read();
to[x1].insert(x2),to[x2].insert(x1);
val[x1]+=a,val[x2]+=b;
}
dfs(1,1);
to[edge.fi].erase(edge.se);
to[edge.se].erase(edge.fi);
root=edge.fi;
DP(root,root);
ans=min(c[root][1],ans);
for(rr int i=1;i<=n;i++) c[i][0]=c[i][1]=0;
root=edge.se;
DP(root,root);
ans=min(ans,c[root][1]);
printf("%lld",ans);
}

T3:玄学题

基本思路

  当我看到-1的时候就已经想到要判指数的奇偶了,但是不会高效地判。

  其实想来也很简单(事后诸葛亮),\(d\)为偶数的情况不决定奇偶,只有奇数的情况才决定,而约数数为奇数只有完全平方数一种情况。

  证明:

对于每个数,他本身以及1都是他的约数,这就有两个约数了。

假如一个数能被某个数整除,这个数及得数都是其约数,那么,除了某个数平方是这个数,那么他的约数一定成对存在。

而假如有个数的平方是这个数,那么它的商还是他自己,那么他这一“对”就只有他自己。

这个平方数约数个数为奇数。

  假如有\(i=p*x^2\)

  要想使\(d(i*j)\)是奇数那么j必须满足\(j=p*y^2\),那么我们只要统计\([1,m]\)内有几个j满足条件即可。

  首先我们可以写一个线性筛法求出每个i的p,并记录。

  线性筛法:

    p[1]=1;
for(rr int i=2;i<=en;i++)
p[i*i]=1;
for(rr int i=2;i<=n;i++)
if(!p[i])
{
p[i]=i;
for(rr int j=2;j<=en;j++)
{
if(j*j*i>n) break;
p[j*j*i]=i;
}
}

  然后,就有j的个数

\[num=
\]
\[\sqrt{\frac{m}{p}}
\]

  证明:

平方在[1,m]范围内的数最多有\(\sqrt{m}\)个那么平方后\(p\)倍还在范围内的就有\(\sqrt{\frac{m}{p}}\)个

  那么我们的算法就是\(O(n)\)的了。

  代码:

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
const int SIZE=1e7+4;
int n,ans;
ll m;
int p[SIZE];
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='-';
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read*10)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace STD;
int main()
{
n=read(),m=read();
int en=sqrt(n);
p[1]=1;
for(rr int i=2;i<=en;i++)
p[i*i]=1;
for(rr int i=2;i<=n;i++)
if(!p[i])
{
p[i]=i;
for(rr int j=2;j<=en;j++)
{
if(j*j*i>n) break;
p[j*j*i]=i;
}
}
for(rr int i=1;i<=n;i++)
{
ll x=sqrt(m/(ll)p[i]);
if(x&1ll) ans--;
else ans++;
}
printf("%d",ans);
}

最新文章

  1. Java线程:线程的同步-同步方法
  2. Objective-C Runtime 运行时之三:方法与消息
  3. 【HDOJ】3183 A Magic Lamp
  4. 模拟jquery封装选择器
  5. iOS开发之基于parse的登录注册
  6. windows后台服务程序编写
  7. webpack基础打包安装分享
  8. [OI]Noip 2018总结(普及)
  9. HTML基础之HTML标签-html header(meta,title) html body(p,br,h,form,div,span,input,lable)
  10. enumerate使用
  11. 浏览器环境下JavaScript脚本加载与执行探析之代码执行顺序
  12. java的基本数据类型默认值
  13. python 抓取alexa数据
  14. ios 让两个tableView同时处于选中状态
  15. Centos7服务器搭建VNC Server环境
  16. 514. Freedom Trail
  17. 批量处理任务进度条控制—基于BackgroundWorker
  18. spark DiskBlockManager
  19. Celery-4.1 用户指南: Application(应用)
  20. 机器学习--近邻成分分析(NCA)算法 和 度量学习

热门文章

  1. 3.python编程与计算机的关系,如何执行python文件
  2. 计算机网络part2——物理层
  3. UI:PointerEventData
  4. 1549页Android最新面试题含答案
  5. Kong网关安装之Docker版(1)
  6. Configure Hosts with Puppet
  7. noip模拟44[我想我以后会碰见计数题就溜走的]
  8. 入门Kubernetes-StatefulSets
  9. noip15
  10. java简体(繁体)转换器