5/15测试
写在前面
我太菜了
T1 方程的解(equation.cpp)
题目大意1
对于不定方程\(a_1+a_2+…a_{k-1}+a_k=g(x)\),其中\(k≥2\)且\(k∈N^*\),\(x\) 是正整数,\(g(x)=x^x mod 1000, x,k\)是给定的数。我们要求的是这个不定方程的正整数解组数。
对于\(40\%\)的数据,\(ans≤10^{16};\)
对于\(100\%\)的数据,\(k≤100,x≤2^{31}-1,k≤g(x)\)
Sol1
第一步求\(g(x)\),很简单的快速幂。
然后题目就转化为了\(g(x)\)个苹果放入\(k\)个不同的盘中,求方案数。标准的插板法,答案就是\(C_{g(x)-1}^{k-1}\)。
由于我太菜了不确定自己写的扩欧对不对,加上数据小,直接用递推式\(C_i^j=C_{i-1}^{j-1}+C_{i-1}^{j}\),\(40\)分到手。
剩下的六十分套一个高精加就可以了,扩欧求要用高精乘。然而我菜,估算得\(ans_{max}≈10^{157}\),所以淼一个手写\(INT\_576\)即可。
代码超\(\%80\)都是复制粘贴,放上精华部分
f[upo][dowm][0]=f[upo-1][dowm-1][0]+f[upo][dowm-1][0];//懒得写高精,淼一个高低位爬了
f[upo][dowm][1]=f[upo-1][dowm-1][1]+f[upo][dowm-1][1]+f[upo][dowm][0]/exc;
f[upo][dowm][2]=f[upo-1][dowm-1][2]+f[upo][dowm-1][2]+f[upo][dowm][1]/exc;
f[upo][dowm][3]=f[upo-1][dowm-1][3]+f[upo][dowm-1][3]+f[upo][dowm][2]/exc;
f[upo][dowm][4]=f[upo-1][dowm-1][4]+f[upo][dowm-1][4]+f[upo][dowm][3]/exc;
f[upo][dowm][5]=f[upo-1][dowm-1][5]+f[upo][dowm-1][5]+f[upo][dowm][4]/exc;
f[upo][dowm][6]=f[upo-1][dowm-1][6]+f[upo][dowm-1][6]+f[upo][dowm][5]/exc;
f[upo][dowm][7]=f[upo-1][dowm-1][7]+f[upo][dowm-1][7]+f[upo][dowm][6]/exc;
f[upo][dowm][8]=f[upo-1][dowm-1][8]+f[upo][dowm-1][8]+f[upo][dowm][7]/exc;
f[upo][dowm][7]%=exc;
f[upo][dowm][6]%=exc;
f[upo][dowm][5]%=exc;
f[upo][dowm][4]%=exc;
f[upo][dowm][3]%=exc;
f[upo][dowm][2]%=exc;
f[upo][dowm][1]%=exc;
f[upo][dowm][0]%=exc;
这里简单提一下,所谓手写就是开一维数组,把一个位数很多的数拆开,每\(18\)位存在数组的一个下标里。对于像\(NOIP2020T1\)就适用,主要优势就是好写。
T2 礼物(gift.cpp)
题目大意2
商店里一共有种礼物。夏川每得到一种礼物,就会获得相应喜悦值\(W_i\)(每种礼物的喜悦值不能重复获得)。
每次,店员会按照一定的概率\(P_i\)(或者不拿出礼物),将第\(i\)种礼物拿出来。
季堂每次都会将店员拿出来的礼物买下来。没有拿出来视为什么都没有买到,也算一次购买。
求夏川能获得的最大喜悦值以及使夏川获得最大喜悦值的期望购买次数。
对于\(100\%\)的数据,\(N≤20\;,0<W_i ≤ 10^9\;,0 < P_i ≤ 1\)且\(\sum_{P_i}≤1\)
Sol2
显然最大喜悦值就是所有礼物喜悦值之和。
那么问题就转化为了把所有物品都抽到一遍的期望值。
由于\(n\)很小,考虑状压。
显然就是这样
inline double f(int now)
{
if(now==0)return 0.0;
if(dp[now])return dp[now];
double ans=1,pn=0;
for(int i=0;i<20;i++)
{
if(mi[i]&now)
{
ans+=p[i+1]*f(mi[i]^now);
pn+=p[i+1];
}
}
return dp[now]=ans/pn;
}
我太菜了解释不来状压方程。
T3 通讯(message.cpp)
题目大意3
\(SERN\)共有\(N\)个部门(总部编号为\(0\)),通讯网络有\(M\)条单向通讯线路,每条线路有一个固定的通讯花费\(C_i\)。
为了保密,消息的传递只能按照固定的方式进行:从一个已知消息的部门向另一个与它有线路的部门传递(可能存在多条通信线路)。我们定义总费用为所有部门传递消息的费用和。
幸运的是,如果两个部门可以直接或间接地相互传递消息(即能按照上述方法将信息由\(X\)传递到\(Y\),同时能由\(Y\)传递到\(X\)),我们就可以忽略它们之间的花费。
由于资金问题(预算都花在粒子对撞机上了),\(SERN\)总部的工程师希望知道,达到目标的最小花费是多少。
Sol3
很显然,缩点\(+DAG\)上的\(DP\)(我太菜了不知道叫什么算法)
题目本身很板,也没什么好写的,就主要提一下注意事项
- 存点存边数组要开两倍
- 从 零 开 始(啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊)
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
struct edge
{
int to,next,v;
}e[200010];
int ei,h[100010];
inline void add(int x,int y,int v)
{
e[++ei]=(edge){y,h[x],v};
h[x]=ei;return;
}
int n,m,dfn[50010],low[50010],swc,te[50010],tid;
int st[100010],tail,dis[100001];
bool vis[100010];
inline void tarjan(int rt)
{
dfn[rt]=low[rt]=++swc;
st[++tail]=rt;
for(int i=h[rt];i;i=e[i].next)
{
int to=e[i].to;
if(!dfn[to])
{
tarjan(to);
low[rt]=min(low[rt],low[to]);
}else if(!te[to])low[rt]=min(low[rt],dfn[to]);
}
if(low[rt]==dfn[rt])
{
tid++;
while(st[tail]!=rt)
{
te[st[tail]]=tid;
for(int i=h[st[tail]];i;i=e[i].next)
{
add(tid,e[i].to,e[i].v);
}
tail--;
}
te[rt]=tid;
for(int i=h[rt];i;i=e[i].next)add(tid,e[i].to,e[i].v);
tail--;
}
return;
}
inline void dotarjan()
{
for(int i=0;i<n;i++)
{
if(!dfn[i])tarjan(i);
}
return;
}
queue<int>qu;
inline void topsort(int rt)
{
qu.push(rt);
dis[rt]=0;vis[rt]=1;
while(!qu.empty())
{
int x=qu.front();qu.pop();vis[x]=0;
for(int i=h[x];i;i=e[i].next)
{
int to=te[e[i].to],v=e[i].v;
if(to==x)continue;
dis[to]=min(dis[to],v);
if(!vis[to])vis[to]=1,qu.push(to);
}
}
}
inline void dotopsort()
{
topsort(te[0]);
int ans=0;
for(int i=n+1;i<=tid;i++)
{
ans+=dis[i];
}
printf("%lld\n",ans);
}
inline void clear()
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(te,0,sizeof(te));
memset(h,0,sizeof(h));
swc=ei=tid=0;
}
signed main()
{
freopen("message.in","r",stdin);
freopen("message.out","w",stdout);
while(1)
{
n=read(),m=read();
if(n==0&&m==0)return 0;
clear();
for(int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z);
}
tid=n;
dotarjan();
dotopsort();
}
return 0;
}
T4 奇袭(raid.cpp)
题目大意4
此题有加强版(实际影响不大)CF526F Pudding Monsters
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个\(N×N\)的网格图,一共有\(N\)支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个\(k×k\)(\(1≤k≤N\))的子网格图包含恰好\(k\)支军队,我们袭击的难度就会增加\(1\)点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样的。
对于\(30\%\)的数据,\(N ≤ 100\)
对于\(60\%\)的数据,\(N ≤ 5000\)
对于\(100\%\)的数据,\(N ≤ 50000\)
Sol4
不会真的有人只恰30的烂分吧
加粗的那条性质非常重要,由此可以直接得到\(60pts\)的算法——\(O(n^2)\)的单调队列
枚举矩阵长度\(i\),然后用单调队列维护长为\(i\)的区间里的最大最小值,每次求差查看是否符合\(Max_i-Min_i=i-1\),符合就把\(ans+=1\)即可。
inline int dddl(int le)
{
ha=hi=0;ta=ti=-1;
int cnt=0;
for(int i=1;i<le;i++)
{
while(ha<=ta&&qua[ta]<h[i])ta--;
while(hi<=ti&&qui[ti]>h[i])ti--;
qua[++ta]=qui[++ti]=h[i];
ina[ta]=ini[ti]=i;
}
for(int i=le;i<=n;i++)
{
while(ha<=ta&&i-ina[ha]>=le)ha++;
while(hi<=ti&&i-ini[hi]>=le)hi++;
while(ha<=ta&&qua[ta]<h[i])ta--;
while(hi<=ti&&qui[ti]>h[i])ti--;
qua[++ta]=qui[++ti]=h[i];
ina[ta]=ini[ti]=i;
if(qua[ha]-qui[hi]==le-1)cnt++;
}
return cnt;
}
接下来是正解,可以参考这篇博文
我太菜了实在不知道该如何解释,就这样吧嗯嗯。
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
struct node
{
int mi,tag,cnt;
}tr[1200010];
struct chess
{
int x,y;
bool operator<(const chess &X)const
{
return x<X.x;
}
}c[300010],sta[300010],sti[300010];
int n,ta,ti;
int ans;
inline void build(int i,int l,int r)
{
if(l==r)
{
tr[i].mi=l;tr[i].cnt=1;return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tr[i]=tr[i<<1];
}
inline void pushdown(int i)
{
int ls=i<<1,rs=ls|1;
tr[ls].mi+=tr[i].tag;tr[rs].mi+=tr[i].tag;
tr[ls].tag+=tr[i].tag;tr[rs].tag+=tr[i].tag;
tr[i].tag=0;
}
void pushup(int i)
{
int ls=i<<1,rs=ls|1;
tr[i].mi=min(tr[ls].mi,tr[rs].mi);tr[i].cnt=0;
if(tr[i].mi==tr[ls].mi)tr[i].cnt+=tr[ls].cnt;
if(tr[i].mi==tr[rs].mi)tr[i].cnt+=tr[rs].cnt;
}
void update(int i,int l,int r,int x,int y,int val)
{
if(l>=x&&r<=y)
{
tr[i].mi+=val;
tr[i].tag+=val;
return;
}
pushdown(i);
int mid=(l+r)>>1;
if(mid>=x)update(i<<1 ,l,mid ,x,y,val);
if(mid<y) update(i<<1|1,mid+1,r,x,y,val);
pushup(i);
}
int query(int i,int l,int r,int x,int y)
{
if(l>=x&&r<=y)return tr[i].mi==0?tr[i].cnt:0;
int mid=(l+r)>>1,ans=0;pushdown(i);
if(mid>=x)ans+=query(i<<1 ,l,mid ,x,y);
if(mid<y) ans+=query(i<<1|1,mid+1,r,x,y);
return ans;
}
signed main()
{
// freopen("raid.in","r",stdin);
// freopen("raid.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
{
c[i].x=read();
c[i].y=read();
}
sort(c+1,c+n+1);
build(1,1,n);
for(int i=1;i<=n;i++)
{
update(1,1,n,1,n,-1);
while(ta&&sta[ta].y<c[i].y)update(1,1,n,sta[ta-1].x+1,sta[ta].x,c[i].y-sta[ta].y),--ta;
sta[++ta]=(chess){i,c[i].y};
while(ti&&sti[ti].y>c[i].y)update(1,1,n,sti[ti-1].x+1,sti[ti].x,sti[ti].y-c[i].y),--ti;
sti[++ti]=(chess){i,c[i].y};
ans+=query(1,1,n,1,i);
}
printf("%lld\n",ans);
return 0;
}
没了

浙公网安备 33010602011771号