四连测Day4
四连爆炸
卡我常数
好像被AluminumGod拉到了创客...哇我这个天天爆炸的水平可能会被其他三位dalao吊起来打
orz Edmond-Karp_XiongGod
orz Deidara_WangGod
orz Small_ChickenGod
T1 树上有一些关键点,求包含i个关键点的联通块数,0<=i<=m
n,m<=1000
树形dp,强行证明复杂度
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f; } const int maxn = 1010,mod = 998244353; vector<int> G[maxn]; int n,m;int a[maxn]; int state; LL f[maxn][maxn],size[maxn],ans[maxn]; inline void dp(int x,int fa) { size[x] = 1; if(a[x] == 1)f[x][1] = 1; else f[x][0] = 1; for(auto to : G[x]) { if(to == fa)continue; dp(to,x);size[x] += size[to]; for(int i=size[x];i>=0;i--) { for(int j=0;j<=i && j<=size[to];j++) (f[x][i] += 1ll * f[to][j] * f[x][i - j]) %= mod; } } for(int i=0;i<=size[x];i++)(ans[i] += f[x][i]) %= mod; } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); n = read(),m = read(); for(int i=1;i<=m;i++)a[read()] = 1; for(int i=1;i<n;i++) { int u = read(),v = read(); G[u].push_back(v);G[v].push_back(u); } dp(1,1); for(int i=0;i<=m;i++)printf("%lld ",ans[i]); }
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #define MAXN 131072 #define MOD 998244353 using namespace std; inline int read() { int x=0,t=1,c; while(!isdigit(c=getchar()))if(c=='-')t=-1; while(isdigit(c))x=x*10+c-'0',c=getchar(); return x*t; } int first[1024],nxt[2048],targ[2048],cnte=1; bool special[1024]; int ans[1024]; int dp[1024][1024],cntd[1024]; int poly[1024]; void AddEdge(int u,int v) { targ[cnte]=v;nxt[cnte]=first[u];first[u]=cnte++;swap(u,v); targ[cnte]=v;nxt[cnte]=first[u];first[u]=cnte++; } void DP(int x,int Fa) { if(special[x]) { dp[x][1]=1;cntd[x]=2; } else { dp[x][0]=1;cntd[x]=1; } for(int i=first[x];i;i=nxt[i]) { if(targ[i]==Fa)continue; int y=targ[i]; DP(y,x); for(int i=0;i<cntd[x]+cntd[y]-1;i++)poly[i]=0; for(int i=0;i<cntd[x];i++) for(int j=0;j<cntd[y];j++) (poly[i+j]+=(long long)dp[x][i]*dp[y][j]%MOD)%=MOD; cntd[x]+=cntd[y]-1; for(int i=0;i<cntd[x];i++)dp[x][i]=poly[i]; } for(int i=0;i<cntd[x];i++)(ans[i]+=dp[x][i])%=MOD; dp[x][0]++;dp[x][0]%=MOD; } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); int n=read(),m=read(); for(int i=0;i<m;i++)special[read()]=1; for(int i=1;i<n;i++)AddEdge(read(),read()); DP(1,0); for(int i=0;i<=m;i++)printf("%d%c",ans[i],i==m?'\n':' '); }
T2
每个机器人对每个零件有一个需求度,现在你有$n$个机器人$m$个零件,第$i$个机器人需要$ki$个零件,求所有机器人需求度和的最大值
费用流
S->机器人 caps = ki,cost = 0
机器人->零件 caps = 1,cost = 需求度
零件->T caps = 1,cost = 0
然后跑最大费用最大流
#include<bits/stdc++.h> using namespace std; const int maxn = 210 * 110 * 4; long long ans;//此处ans保存费用 inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f; } int n,m,s,t; int first[maxn],to[maxn],nx[maxn],caps[maxn],cost[maxn],cnt=-1; int vis[maxn],dis[maxn]; deque<int> dq; inline void add(int u,int v,int w,int c) { to[++cnt]=v; nx[cnt]=first[u]; first[u]=cnt; caps[cnt]=w; cost[cnt]=c; } inline void ins(int u,int v,int w,int c){add(u,v,w,c);add(v,u,0,-c);} inline int afps(int s,int t)//反向spfa { memset(vis,0,sizeof(vis)); memset(dis,127,sizeof(dis)); dis[t]=0;vis[t]=1; dq.clear(); dq.push_back(t); while(!dq.empty()) { int now=dq.front();dq.pop_front(); for(int i=first[now];i>-1;i=nx[i]) if(caps[i^1] && dis[to[i]]>dis[now]-cost[i]) { dis[to[i]]=dis[now]-cost[i]; if(!vis[to[i]]) { vis[to[i]]=1; if(!dq.empty() && dis[to[i]]<dis[dq.front()])dq.push_front(to[i]); else dq.push_back(to[i]); } } vis[now]=0; } return dis[s]<2139062143; } inline int dfs(int u,int flow) { if(u==t){vis[t]=1;return flow;} int used=0,tmp;vis[u]=1; for(int i=first[u];i>-1;i=nx[i]) if(!vis[to[i]] && caps[i] && dis[u]-cost[i]==dis[to[i]]) { tmp=dfs(to[i],min(caps[i],flow-used)); if(tmp)ans+=1ll*tmp*cost[i],caps[i]-=tmp,caps[i^1]+=tmp,used+=tmp; if(used==flow)break; } return used; } inline int zkw() { long long maxflow=0; while(afps(s,t)) { vis[t]=1; while(vis[t]) { memset(vis,0,sizeof(vis)); maxflow+=(long long)dfs(s,2139062143); } } return maxflow; } int main() { freopen("robot.in","r",stdin); freopen("robot.out","w",stdout); memset(nx,-1,sizeof nx);memset(first,-1,sizeof first); n=read(),m=read();s = 0;t = n + m + 1; for(int i=1;i<=n;i++) { int a = read(); ins(s,i,a,0); } for(int i=1;i<=m;i++)ins(i + n,t,1,0); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int a = read(); ins(i,j + n,1,-a); } } zkw(); printf("%d",-ans); }
需要注意的是这道题图很稠密,EK被卡成了暴力分
T3
有一维空间内$n$个点,编号从$1$到$n$,编号为$i$的点坐标为$x_i$。
现在,请选出编号连续的一些点,使得被选出的所有点到某一点的距离和的最小值不超过一正
整数$m$,问最多选出多少点?
$O(nlog^2n)$的做法:(卡常的出题人嘤嘤嘤qwq)
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); while(!isdigit(ch)){if(ch == '-') f = -1;ch = getchar();} while(isdigit(ch)) x = x * 10 + ch - '0',ch = getchar(); return x * f; } const int maxpos = 1000000; int n,m; int res; int va[maxpos * 4 + 10]; int size[maxpos * 4 + 10]; int a[100005]; #define ls (x << 1) #define rs ((x << 1) | 1) inline void Insert(int x,int l,int r,int val,int f) { if(l == r){size[x] += f;va[x] += (l * f);return;} int mid = (l + r) >> 1; if(val <= mid) Insert(ls,l,mid,val,f); else Insert(rs,mid + 1,r,val,f); va[x] = va[ls] + va[rs]; size[x] = size[ls] + size[rs]; } inline int kth(int x,int l,int r,int val) { if(l == r)return l; int mid = (l + r) >> 1; if(val <= size[ls]) return kth(ls,l,mid,val); else return kth(rs,mid + 1,r,val - size[ls]); } inline int query(int x,int l,int r,int L,int R) { if(L<= l && R >= r){res += size[x];return va[x];} int mid = (l + r) >> 1; int ret = 0; if(L <= mid)ret += query(ls,l,mid,L,R); if(R > mid)ret += query(rs,mid + 1,r,L,R); return ret; } inline int check(int mid) { memset(va,0,sizeof(va));memset(size,0,sizeof(size)); for(int i = 1;i <= mid;i++)Insert(1,1,maxpos,a[i],1); int pos = kth(1,1,maxpos,(mid + 1) >> 1); res = 0; int ans = query(1,1,maxpos,1,pos); if( ( ( res * pos ) - ans ) + ( va[1] - ans) - ( (mid - res ) * pos) <= m) return 1;//!!! for(int i = mid + 1;i<=n;i++) { Insert(1,1,maxpos,a[i - mid], - 1); Insert(1,1,maxpos,a[i],1); pos = kth(1,1,maxpos,(mid + 1) >> 1); res = 0; ans = query(1,1,maxpos,1,pos); if( ( ( res * pos ) - ans ) + ( va[1] - ans) - ( (mid - res ) * pos) <= m) return 1;//!!! } return 0; } int main() { freopen("choose.in","r",stdin); freopen("choose.out","w",stdout); n = read(),m = read(); for(int i = 1;i <= n;i++)a[i] = read(); int l = 0,r = n,ans; while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) {l = mid + 1;ans = mid;} else r = mid - 1; } printf("%d\n",ans); }
$O(nlogn)$的做法:(放份好看的)
。。。还是来写一下标算的做法吧
可以知道到“某一点距离和的最小值”一定是这几个点的中位数
维护一个滑动窗口,每次用平衡树查询窗口内的答案,看合不合法,处理出以每个点开头/结尾的滑窗的最长长度即可
#include<iostream> #include<cmath> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<queue> #include<vector> #include<set> #define MAXN 100100 #define MAXX 1001000 #define ll long long #define inf 2139062143 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,l,ans,g[MAXN],val[MAXX<<2],maxn; ll sum[MAXX<<2]; void mdf(int k,int l,int r,int x,int p) { if(l==r) {sum[k]+=p*x,val[k]+=p;return ;} int mid=(l+r)>>1; if(x<=mid) mdf(k<<1,l,mid,x,p); else mdf(k<<1|1,mid+1,r,x,p); sum[k]=sum[k<<1]+sum[k<<1|1],val[k]=val[k<<1]+val[k<<1|1]; } ll Ask(int k,int l,int r,int x) { if(l==r) return val[k]*maxn+l; int mid=(l+r)>>1; if(val[k<<1]>=x) return Ask(k<<1,l,mid,x); else return Ask(k<<1|1,mid+1,r,x-val[k<<1]); } ll query(int k,int l,int r,int a,int b) { if(a>b) return 0; if(l==a&&r==b) return sum[k]; int mid=(l+r)>>1; if(b<=mid) return query(k<<1,l,mid,a,b); else if(a>mid) return query(k<<1|1,mid+1,r,a,b); else return query(k<<1,l,mid,a,mid)+query(k<<1|1,mid+1,r,mid+1,b); } int lft(int k,int l,int r,int a,int b) { if(a>b) return 0; if(l==a&&r==b) return val[k]; int mid=(l+r)>>1; if(b<=mid) return lft(k<<1,l,mid,a,b); else if(a>mid) return lft(k<<1|1,mid+1,r,a,b); else return lft(k<<1,l,mid,a,mid)+lft(k<<1|1,mid+1,r,mid+1,b); } ll calc(int x) { if(x==l) return 0; if(x-l==1) return abs(g[x]-g[l]); ll t=(x-l+1)/2+1,k=Ask(1,1,maxn,t),lv=lft(1,1,maxn,1,k%maxn-1),q= k/maxn+2*lv-x+l-1;k%=maxn; return query(1,1,maxn,k+1,maxn)-query(1,1,maxn,1,k-1)+q*k; } int main() { freopen("choose.in","r",stdin); freopen("choose.out","w",stdout); n=read(),m=read(); for(int i=1;i<=n;i++) g[i]=read(),maxn=max(maxn,g[i]); for(int i=l=1;i<=n;i++) { mdf(1,1,maxn,g[i],1); while(calc(i)>m) {mdf(1,1,maxn,g[l],-1);l++;} ans=max(ans,i-l+1); } printf("%d",ans); }
出题人为了卡我的常,甚至使用了前两题开O2这题临时不开的技巧...
嘤