(联考)noip89
T1
签到题
发现 \(c=a-b\) 不会很大,直接枚举 \(c\) ,一个可行的边界是 \(\sqrt[c]{n}\) 在其附近枚举 \(b\) , \(a\) 对应的加上 \(c\) 即可。
T2
签到题。
设 \(m=\frac{n}{k}\) 。
先判掉 \(k=1\) 以及无解的情况。
无解包括:\(m=1,n\neq1 \vee n\;is\;even,\;m\;is\;odd\)
当 \(m\) 为偶数时,直接一条龙走下来。以 \(n=12,k=3\) 为例:
当 \(m\) 为奇数时,只需要让前3列相当,后 \(m-3\) 列可以一条龙走下来,一种可行的构造方案为,第一列从 \([1,k]\) 顺着填下去,第二列填完数,将对应行的数相加排完序后能形成一个公差为1的等差数列,第三列对应填让和相等即可。 以 \(n=21,k=7\) 为例:
应该没出错
注意亿些细节。
T3
30pts:暴力。
80/100pts:
先转换题意:题目实际上是让求 \(x\) 位置上最早 \(\ge y\) 的时间。
于是可以直接用主席树暴力算,常数过于巨大,于是只有80pts。
对询问离线,对于修改 \([l,r]\) 的操作,在 \(l\) 存一个 \((h,i)\) ,在 \(r+1\) 存一个 \((-h,i)\) ,并存下来每个位置对应的查询操作。
然后去扫原序列,并开一颗以询问编号为下标的树状数组,扫到点 \(i\) 时,先进行修改操作,如果为一个修改区间的起点,那么就会有 \(h\) 的贡献,为终点靠后的一个位置,则会有 \(-h\) 去消除前面的影响,然后查询。
查询可以通过二分树状数组解决,树状数组二分是通过 \(query(mid)\) 来确定上下界变化。
Code
#include<cstdio>
#include<cctype>
#include<vector>
using std::vector;
#define re register
#define pack push_back
const int MAX = 1e6+3;
#define int long long
#define scanf oma=scanf
#define freopen file=freopen
int oma;
FILE* file;
namespace some
{
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
bool w=0; s=0; char ch=getchar();
while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
return s=w?-s:s,*this;
}
}cin;
int n,q,ans[MAX];
struct my
{ int a,b; };
vector<my>q1[MAX],q2[MAX];
#define debug(s) printf("%s\n",s)
}using namespace some;
namespace Binary_Index_Tree
{
struct BIT
{
int val[MAX];
#define lowbit(x) x&-x
void modify(int x,int w)
{ for(re int i=x; i<=q; i+=lowbit(i)) { val[i] += w; } }
int query(int x,int res = 0)
{ for(re int i=x; i; i-=lowbit(i)) { res += val[i]; } return res; }
}Tree;
}using namespace Binary_Index_Tree;
namespace OMA
{
auto main = []() -> signed
{
//#define local
#ifdef local
debug("! look here,if you want to submit,please closed this");
freopen("node.in","r",stdin); //freopen("my.out","w",stdout);
#endif
freopen("concrete.in","r",stdin); freopen("concrete.out","w",stdout);
cin >> n >> q;
for(re int i=1,opt,l,r,h,x,y; i<=q; i++)
{
ans[i] = -1;
cin >> opt;
if(opt==1)
{ cin >> l >> r >> h; q1[l].pack((my){h,i}),q1[r+1].pack((my){-h,i}); }
else
{ cin >> x >> y; q2[x].pack((my){y,i}); }
}
for(re int i=1,l,r,res; i<=n; i++)
{
for(auto j : q1[i])
{ Tree.modify(j.b,j.a); }
for(auto j : q2[i])
{
l = 1,r = j.b,res = 0;
while(l<=r)
{
int mid = l+r>>1;
if(Tree.query(mid)>=j.a)
{ r = mid-1,res = mid; }
else
{ l = mid+1; }
}
ans[j.b] = res;
}
}
for(re int i=1; i<=q; i++)
{ if(ans[i]!=-1) { printf("%lld\n",ans[i]); } }
//#define judge
#ifdef judge
if(system("diff my.out ans.out -w");)
{ printf("WA\n"); }
else
{ printf("AC\n"); }
#endif
return 0;
};
};
signed main()
{ return OMA::main(); }
T4
\((u,v)\) 堵塞实际上相当于增加了点 \(u\) 的初始污水,减少了点 \(v\) 的初始污水,则 \(\frac{w_{old}}{deg_{u}-1}=\frac{w_{new}}{deg_{u}}\) , \(old\) 是不计堵塞, \(new\) 是算上堵塞的,于是 \(w_{new}=\frac{w_{old}\cdot deg_{u}}{deg_{u}-1}\) 。
那么点 \(u\) 初始增加的污水即为 \(w_{new}-w_{old}\) ,点 \(v\) 减少的污水即为 \(\frac{w_{new}}{deg_{u}}\) 。记得乘上对应管道堵塞的概率。
一遍topo就可以求出每个点的期望初始污水量,带着初始值再跑一遍topo就是最后的期望排出污水量。
Code
#include<queue>
#include<cstdio>
#include<cctype>
using std::queue;
#define re register
const int N = 2e5+3;
const int K = 5e5+3;
#define int long long
#define scanf oma=scanf
const int mod = 998244353;
#define freopen file=freopen
int oma;
FILE* file;
namespace some
{
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
bool w=0; s=0; char ch=getchar();
while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
return s=w?-s:s,*this;
}
}cin;
int n,m,r,k,inv[N];
#define debug(s) printf("%s\n",s)
auto quickpow = [](int a,int b,int res = 1)
{
while(b)
{
if(b&1)
{ res = res*a%mod; }
a = a*a%mod,b >>= 1;
}
return res;
};
}using namespace some;
namespace Graph
{
int deg[N][3],du[N];
struct graph
{
int next;
int to;
int w;
}edge[K];
int cnt=1,head[N];
auto add = [](int u,int v,int w) { edge[++cnt] = (graph){head[u],v,w},head[u] = cnt; };
queue<int>q;
int val[N],delta[N];
auto topo1 = []() -> void
{
inv[0] = quickpow(inv[0],mod-2);
for(re int i=1; i<=n; i++)
{
if(!deg[i][0])
{ val[i] = delta[i] = 1; q.push(i); }
deg[i][1] = deg[i][0],inv[i] = quickpow(du[i],mod-2);
}
while(!q.empty())
{
int u = q.front(); q.pop();
int tmp = du[u]*val[u]%mod*quickpow(du[u]-1,mod-2)%mod;
for(re int i=head[u],v,w; i; i=edge[i].next)
{
v = edge[i].to,w = edge[i].w;
if(--deg[v][1]==0) { q.push(v); }
(val[v] += val[u]*inv[u]%mod) %= mod,
(delta[u] += (tmp-val[u]+mod)%mod*w%mod*inv[0]%mod) %= mod,
delta[v] = (delta[v]-tmp*inv[u]%mod*w%mod*inv[0]%mod+mod)%mod;
}
}
};
auto topo2 = []() -> void
{
for(re int i=1; i<=n; i++)
{
if(!deg[i][0])
{ q.push(i); }
deg[i][2] = deg[i][0];
//printf("%lld\n",val[i]);
}
while(!q.empty())
{
int u = q.front(); q.pop();
for(re int i=head[u],v; i; i=edge[i].next)
{
v = edge[i].to;
if(--deg[v][2]==0) { q.push(v); }
(delta[v] += delta[u]*inv[u]%mod) %= mod;
}
}
for(re int i=n-r+1; i<=n; i++)
{ printf("%lld ",delta[i]); }
};
}using namespace Graph;
namespace OMA
{
auto main = []() -> signed
{
//#define local
#ifdef local
debug("! look here,if you want to submit,please closed this");
freopen("node.in","r",stdin); freopen("my.out","w",stdout);
#endif
freopen("water.in","r",stdin); freopen("water.out","w",stdout);
cin >> n >> m >> r >> k;
for(re int i=1,u,v,w; i<=k; i++)
{ cin >> u >> v >> w; add(u,v,w); deg[v][0]++,du[u]++,(inv[0] += w)%=mod; }
topo1(),topo2();
//#define judge
#ifdef judge
if(system("diff my.out ans.out -w"))
{ printf("WA\n"); }
else
{ printf("AC\n"); }
#endif
return 0;
};
};
signed main()
{ return OMA::main(); }