CSP-S模拟33
前言
挂分原因最智障的一场模拟赛
T1: Divisors(div)
思路:
大签到水题。直接模拟,开个 \(map\) 存就行。
猜猜我为什么挂分?
\(m,n\) 写反了。导致测大样例时一次蓝屏,一次黑屏。谁能想到短短半小时内我能重启两次电脑。最后直接不敢测大样例了,写了个部分分就走了。(其实只要把数组开成 \(map\) 就能过了)
$code$
#include<iostream>
#include<map>
using namespace std;
const int N=205;
int m,n,x,ans[N];map<int,int> mp;
signed main(){
// freopen("div.in","r",stdin);
// freopen("div.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>x;
for(int i=1;i*i<=x;i++){
if(x%i==0){
mp[i]++;
if(i*i!=x) mp[x/i]++;
}
}
}ans[0]=n;
for(auto x:mp) if(x.first<=n) ans[x.second]++,ans[0]--;
for(int i=0;i<=m;i++) cout<<ans[i]<<'\n';
return 0;
}
T2:market
思路:
首先,要按照时间进行排序,这是显然的。
然后就当背包做就行了。由于花费的数据太大,而价值的数据较小,所以我们不妨让价值作为下标,求最小花费。设 \(dp_{i,j}\) 表示 第 \(i\) 时刻 价值为 \(j\) 的最小花费。
最后在 \(upper\_bound\) 一下就好了。
代码:
$code$
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N=310,M=300,MM=90000;
int m,n,dp[N][N*N],l=1,r,t,money;
struct node{
int c,v,t;
bool operator<(const node &css)const{return t<css.t;}
}a[N];
inline int read(){int 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-'0');ch=getchar();}return x*f;}
inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}
inline int min(int x,int y){return (x<y)?x:y;}
signed main(){
// freopen("market.in","r",stdin);
// freopen("market.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++) a[i].c=read(),a[i].v=read(),a[i].t=read();
sort(a+1,a+1+n);
for(int i=0;i<=M;i++) for(int j=0;j<=MM;j++) dp[i][j]=1e18;
dp[0][0]=0;
for(int i=1;i<=M;i++){
while(r<=n&&a[r].t<=i) r++;
for(int j=0;j<=MM;j++) dp[i][j]=dp[i-1][j];
for(int k=l;k<r;k++) for(int j=MM;j;j--) if(j>=a[k].v) dp[i][j]=min(dp[i][j],dp[i][j-a[k].v]+a[k].c);
l=r;
}
for(int i=1;i<=M;i++) for(int j=MM-1;j>=0;j--) if(dp[i][j+1]<dp[i][j]) dp[i][j]=dp[i][j+1];
while(m--){
t=read(),money=read();
int ans=upper_bound(dp[t],dp[t]+MM+1,money)-dp[t]-1;
write(ans);puts("");
}
return 0;
}
/*
5 2
5 5 4
1 3 1
3 4 3
6 2 2
4 3 2
3 8
5 9
*/
T3还是没会ε(┬┬﹏┬┬)3
T4:树 (usmjer)
吐槽:
嗨,调了两天的代码,您猜怎么个事儿?
诶呦,还不是这无向边建错了嘛。您瞅这错也真是有够弱智的嘞。
正确的:
inline void add(int x,int y){
e[++cnt].to=y;e[cnt].nxt=head[x];head[x]=cnt;
e[++cnt].to=x;e[cnt].nxt=head[y];head[y]=cnt;
}
错误的:
inline void add(int x,int y){
e[++cnt].to=y;e[cnt].nxt=head[x];head[x]=cnt;
e[++cnt].to=y;e[cnt].nxt=head[x];head[x]=cnt;
}
我真没招了...
思路:
我们不难得出结论:如果两个点是同一条链上下的关系,那么这两个点之间的路径一定是同向的;反之,则两点到 \(lca\) 的的路径是反向的。
我们考虑将边下放到深度较大的点,并且一个点 \(i\) 拆为两个点 \(i,i+n\) 表示该点两个不同的方向。设两个点 \(x,y\) ,若这两个点方向相同,则连 \((x,y) ~ (x+n,y+n)\) ;反之,则连 \((x,y+n) ~ (x+n,y)\) 。我们可以用并查集来维护这个操作。
然后我们考虑无解的情况。显然,当一条边既向上又向下的时候无解 (这不纯纯废话) 。
那么什么时候一条边既向上又向下呢?
即 \(x,x+n\) 联通的时候。换句话说若两点在同一个并查集内,则无解。
注意事项:
- 并查集父节点和树的父节点不要搞混了
- 求 \(lca\) 的时候一定注意是 \(x\) ,还是 \(top[x]\)
- 建无向边的时候不要粘完就跑,记得改方向呀呜呜呜
代码:
$code$
#include<iostream>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
#define int long long
using namespace std;
const int N=300000+5,mod=1e9+7;
int n,m,u,v,a,b,ans,num,cnt,head[N],fa[N],siz[N],dep[N],son[N],dfn[N],rnk[N],top[N],css[N<<1];
struct malachite{int to,nxt;}e[N<<1];
inline void add(int x,int y){
e[++cnt].to=y;e[cnt].nxt=head[x];head[x]=cnt;
e[++cnt].to=y;e[cnt].nxt=head[x];head[x]=cnt;
}
inline int qpow(int x,int y){
int res=1;
while(y){
if(y&1) res=(res*x)%mod;
x=(x*x)%mod;
y>>=1;
}return res;
}
inline int find(int x){
if(css[x]==x) return x;
return css[x]=find(css[x]);
}
inline void mer(int x,int y){
int p=find(x),q=find(y);
if(p!=q) css[p]=q;
}
inline void merge(int x,int y,bool op){
if(op) mer(x,y+n),mer(x+n,y);
else mer(x,y),mer(x+n,y+n);
}
inline void dfs1(int x){
siz[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa[x]) continue;
dep[y]=dep[x]+1;fa[y]=x;
dfs1(y);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
inline void dfs2(int x,int tp){
dfn[x]=++num;rnk[num]=x;top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to);
}
inline int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(dep[x]<dep[y]) return x;
else return y;
}
struct wutong{int l,r,ch;}tr[N<<2];
inline void build(int id,int l,int r){
tr[id].l=l;tr[id].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
build(lid,l,mid);build(rid,mid+1,r);
}
inline void update(int id,int l,int r){
if(l>r||tr[id].ch) return ;
if(tr[id].l==tr[id].r){
merge(fa[rnk[tr[id].l]],rnk[tr[id].l],0);
tr[id].ch=1;
return ;
}int mid=(tr[id].l+tr[id].r)>>1;
if(l<=mid) update(lid,l,r);
if(r>mid) update(rid,l,r);
tr[id].ch=tr[lid].ch&tr[rid].ch;
}
signed main(){
// freopen("usmjer.in","r",stdin);
// freopen("usmjer.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<n;i++) cin>>u>>v,add(u,v);
for(int i=1;i<=n;i++) css[i]=i,css[i+n]=i+n;
dfs1(1);dfs2(1,1);build(1,1,n);
for(int i=1;i<=m;i++){
cin>>a>>b;
int llca=lca(a,b);
if(a!=llca&&b!=llca) merge(a,b,1);
while(top[llca]!=top[a]){
update(1,dfn[top[a]]+(fa[top[a]]==llca),dfn[a]);
a=fa[top[a]];
}update(1,dfn[llca]+2,dfn[a]);
while(top[llca]!=top[b]){
update(1,dfn[top[b]]+(fa[top[b]]==llca),dfn[b]);
b=fa[top[b]];
}update(1,dfn[llca]+2,dfn[b]);
}
for(int i=2;i<=n;i++){
int x=find(i),y=find(i+n);
if(x==y){
cout<<0<<'\n';
return 0;
}ans+=(i!=x)+(i+n!=y);
}
cout<<qpow(2,n-1-(ans>>1));
return 0;
}
后言
10.19日限定闲话
让我们一起祝 \(QED\) 大佬:
生日快乐呀 ~~
衷心祝愿他今年的愿望都能实现