2021.11.4考试总结[冲刺NOIP模拟22]
继续被踩...
主要是T2做上头了,只给后两题留了1h,而且还没构造出3的方案。。时间分配有很大问题
T1 迷之阶乘
将 \(n\) 拆为 \(i\) 个连续自然数的和,那么这 \(i\) 个数中一定有 \(\left\lfloor\sqrt[i]n\right\rfloor\) 和 \(\left\lceil\sqrt[i]n\right\rceil\)。然后直接爆扫即可。
直接用 pow
开根会爆精度,用 long double
就没事了。
\(code:\)
T1
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
typedef double DB;
typedef long double LD;
int read(){
LL x=0,f=1; char ch=getchar();
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(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
int t,n,lmt;
vector<pair<int,int>>ans;
signed main(){
freopen("factorial.in","r",stdin);
freopen("factorial.out","w",stdout);
t=read();
while(t--){
n=read(); ans.clear();
if(n==1){ puts("-1"); continue; }
for(int i=21;i;i--){
int res=1,tmp=pow((LD)n,(LD)1.0/i);
int l=max(2ll,tmp-i+1),r=l+i-1;
for(int j=l;j<=r;j++) res*=j;
if(res==n){ ans.push_back(make_pair(l-1,r)); continue; }
while(l<tmp&&res<n){
res/=l; res*=(r+1);
++l; ++r;
if(res==n){ ans.push_back(make_pair(l-1,r)); break; }
if(res<0) break;
}
}
write(ans.size(),'\n');
for(auto i:ans) write(i.second,' '),write(i.first,'\n');
}
return 0;
}
T2 子集
特判 \(n=1\wedge k=1\) 的情况。令 \(m=\frac{n}{k}\) ,那么当 \(m=1\) 时显然无解,当 \(n\) 为偶数, \(m\) 为奇数时, \(k\) 不整除 \(\sum_{i=1}^ni\) ,也无解。
接下来,只要能构造出向所有集合加入 \(2\) 或 \(3\) 个元素,是它们和相等的方案,即可构造出解。
加入两个很简单,一条龙加入即可。加入三个时,第一列依次加入,第二列从中间下方开始加入,第三列按差值补上即可。
以 \(k=5\) 为例:
\(code:\)
T2
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL x=0,f=1; char ch=getchar();
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(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
int t,n,m,k;
vector<int>ans[1000010];
void solve_even(int ed){
int tmp;
for(int i=1;i<=k;i++)
for(int j=1;j<=ed;j++){
tmp=(j-1)*k+((j&1)?i:k-i+1);
ans[i].push_back(tmp);
}
}
void solve_odd(int st){
int sum=(st+1+n)*(n-st)/2/k;
for(int i=1;i<=k;i++) ans[i].push_back(++st);
for(int i=(k+1)/2+1;i<=k;i++) ans[i].push_back(++st);
for(int i=1;i<=(k+1)/2;i++) ans[i].push_back(++st);
for(int i=1;i<=k;i++) ans[i].push_back(sum-ans[i][m-2]-ans[i][m-3]);
}
void print(){
for(int i=1;i<=k;i++){
for(int v:ans[i]) write(v,' ');
puts("");
}
for(int i=1;i<=k;i++) ans[i].clear();
}
signed main(){
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
t=read();
while(t--){
n=read(); k=read(); m=n/k;
if(n==1&&k==1){ puts("Yes"); puts("1"); continue;}
if(m==1){ puts("No"); continue; }
if((m&1)&&!(n&1)){ puts("No"); continue; }
puts("Yes");
if(k==1){ for(int i=1;i<=n;i++) write(i,' '); puts(""); continue; }
if(!(m&1)) solve_even(m);
else solve_even(m-3),solve_odd((m-3)*k);
print();
}
return 0;
}
T3 混凝土粉末
转化题意,实际上需要支持区间加与查询单点值达到某值的最早时刻。
题面中有位置与时间两层限制,考虑通过离线去掉一维。区间限制起点终点不确定,时间限制却都是从 \(0\) 开始,于是可以对时间离线操作。区间方面,扫描线可以很轻松地解决。
开以时间为下标的线段树,记录当前扫到的位置在每一时刻的大小,每次查询线段树二分即可。
\(code:\)
T3
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL x=0,f=1; char ch=getchar();
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(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1000010;
int n,Q,tot,ans[NN];
vector<pair<int,int>>add[NN],qry[NN];
namespace Segment_tree{
#define ld rt<<1
#define rd (rt<<1)|1
int mx,res,sum[NN<<2];
void pushup(int rt){ sum[rt]=sum[ld]+sum[rd]; }
void insert(int rt,int l,int r,int pos,int val){
if(l==r) return sum[rt]+=val,void();
int mid=l+r>>1;
if(pos<=mid) insert(ld,l,mid,pos,val);
else insert(rd,mid+1,r,pos,val);
pushup(rt);
}
void Search(int rt,int l,int r,int ext){
if(~res||l>ext) return;
if(r<=ext){
if(l==r) sum[rt]>=mx?res=l:mx-=sum[rt];
else{
int mid=l+r>>1;
if(sum[ld]>=mx) Search(ld,l,mid,ext);
else mx-=sum[ld],Search(rd,mid+1,r,ext);
}
return;
}
int mid=l+r>>1;
Search(ld,l,mid,ext);
Search(rd,mid+1,r,ext);
}
} using namespace Segment_tree;
signed main(){
freopen("concrete.in","r",stdin);
freopen("concrete.out","w",stdout);
n=read(); Q=read();
memset(ans,-1,sizeof(ans));
for(int i=1;i<=Q;i++){
int op=read();
if(op==1){
int x=read(),y=read(),h=read();
add[x].push_back(make_pair(h,i));
add[y+1].push_back(make_pair(-h,i));
} else{
int x=read(),y=read();
qry[x].push_back(make_pair(y,i));
}
}
for(int i=1;i<=n;i++){
for(auto v:add[i]) insert(1,1,Q,v.second,v.first);
for(auto q:qry[i]){
res=-1; mx=q.first;
Search(1,1,Q,q.second);
ans[q.second]=max(res,0ll);
}
}
for(int i=1;i<=Q;i++) if(~ans[i]) write(ans[i],'\n');
return 0;
}
T4 排水系统
不删边的答案直接拓扑排序。
考虑删去一条边 \((u\to v)\) 之后对图的变化,可以看作 \(v\) 点的值减少, \(u\) 点的其它出点值增加。而这些值增加或减少 \(x\) ,等价于这些位置在开始时的值为 \(x\) 或 \(-x\) 。
而由于 \(u\) 的出点值的增加量是均匀的,因此它们值的变化又能转化为 \(u,v\) 值的变化。因此处理出初始时所有点的期望值,再拓扑一遍就能求出答案。
\(code:\)
T4
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL x=0,f=1; char ch=getchar();
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(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=200010,MM=500010,mod=998244353;
int n,m,r,k,sum,p[MM],ans[NN],in[NN],out[NN],deg[NN];
int idx,st[MM],to[MM],nex[MM],head[NN];
int fin[NN];
int inl[1010][1010];
void add(int a,int b,int c){ to[++idx]=b; st[idx]=a; nex[idx]=head[a]; head[a]=idx; p[idx]=c; ++deg[b]; ++out[a]; (sum+=c)%=mod; }
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%mod;
a=a*a%mod;
}
return res;
}
queue<int>q;
void topo(){
memcpy(in,deg,sizeof(deg));
for(int i=1;i<=m;i++) ans[i]=1,q.push(i);
while(!q.empty()){
int x=q.front(); q.pop();
for(int i=head[x],v=to[i];i;i=nex[i],v=to[i]){
--in[v];
(ans[v]+=ans[x]*qpow(out[x],mod-2))%=mod;
if(!in[v]) q.push(v);
}
}
}
void topo_sort(){
memcpy(in,deg,sizeof(deg));
for(int i=1;i<=m;i++) fin[i]=1,q.push(i);
for(int i=1;i<=idx;i++){
int tmp=ans[st[i]]*qpow(out[st[i]],mod-2)%mod*p[i]%mod*qpow(sum,mod-2)%mod;
int pmt=ans[st[i]]*qpow(out[st[i]]-1,mod-2)%mod*p[i]%mod*qpow(sum,mod-2)%mod;
(fin[to[i]]+=mod+mod-pmt)%=mod;
(fin[st[i]]+=out[st[i]]*(mod+pmt-tmp)%mod)%=mod;
}
while(!q.empty()){
int x=q.front(); q.pop();
for(int i=head[x],v=to[i];i;i=nex[i],v=to[i]){
--in[v];
(fin[v]+=fin[x]*qpow(out[x],mod-2))%=mod;
if(!in[v]) q.push(v);
}
}
}
signed main(){
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
n=read(); m=read(); r=read(); k=read();
for(int a,b,c,i=1;i<=k;i++)
a=read(),b=read(),c=read(),add(a,b,c);
topo(); topo_sort();
for(int i=n-r+1;i<=n;i++) write(fin[i],' ');
return puts(""),0;
}