1004 Distribution of books
首先应该很容易猜到要二分答案
那么如何check这个上限x?
设f[i]f[i]f[i]表示前iii本书最多能分成的多少堆
那么有f[i]=max{f[j]}+1f[i]=max\{f[j]\}+1f[i]=max{f[j]}+1,其中jjj要保证sum[i]−sum[j]<=xsum[i]-sum[j]<=xsum[i]−sum[j]<=x
提前对前缀和离散化并且建立线段树
二分sum[j]sum[j]sum[j]最大可以取到多少,然后就可以查询区间最大值了
1006 Fansblog
首先由质数的密度分布可以大力猜测这个质数QQQ离PPP应该不会很远,所以暴力从大到小枚举就好
判断是不是质数可以用MillerRobinMiller RobinMillerRobin测试
然后用一下威尔逊定理:当且仅当ppp是质数时,(p−1)!≡−1(mod(p-1)!\equiv -1(mod(p−1)!≡−1(mod p)p)p)
所以用逆元搞一下就好了
1007 Find the answer
实际上不需要什么骚操作,暴力找出若干个最大的变成0就好
可以用线段树做,线段树里从大到小存了前缀
每次判断所有数的和减去左半区间的数后时候是否依然大于mmm
如果大于说明需要继续变成0,减一减继续往右区间递归
否则说明已经符合要求,往左递归
当然似乎并没有卡掉两个log的做法:二分位置然后线段树直接查询前缀和
#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define N 300000
#define ll long long
using namespace std;
struct www{int a,i;} f[N];
int T,n,m,i,res;
ll s;
int a[N],c[N];
long long sum[N*4];
int num[N*4];
bool cmp(const www &x,const www &y) {return x.a > y.a;}
void build(int rt,int l,int r)
{
sum[rt] = num[rt] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
}
void query(int rt,int l,int r,ll s)
{
if (l == r)
{
res+=num[rt];
return;
}
int mid = (l + r) >> 1;
if (s - sum[rt<<1] <= m)
query(rt<<1,l,mid,s);
else
{
res += num[rt<<1];
query(rt<<1|1,mid+1,r,s-sum[rt<<1]);
}
}
void update(int rt,int l,int r,int pos,ll v)
{
if (l == r)
{
sum[rt] = v; num[rt] = 1;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) update(rt<<1,l,mid,pos,v);
else update(rt<<1|1,mid+1,r,pos,v);
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
num[rt] = num[rt<<1] + num[rt<<1|1];
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&a[i]);
fo(i,1,n) f[i].a = a[i];
fo(i,1,n) f[i].i = i;
sort(f+1,f+n+1,cmp);
fo(i,1,n) c[f[i].i] = i;
s = 0;
build(1,1,n);
fo(i,1,n)
{
s = s + a[i]; res = 0;
query(1,1,n,s);
printf("%d ",res);
update(1,1,n,c[i],a[i]);
}
printf("\n");
}
return 0;
}
1009 K subsequence
费用流
只能取kkk次:源点sss拆成s0s_0s0和s1s_1s1,之间的流量为kkk
每个点只能取一次:xix_ixi拆成xi1x_{i1}xi1和xi2x_{i2}xi2,之间的流量为1
和最大:xi1x_{i1}xi1和xi2x_{i2}xi2之间的费用为−ai-a_i−ai
连边:若i<ji<ji<j且ai≤aja_i \le a_jai≤aj,说明aja_jaj可以跟在aia_iai后面,所以在xi2x_{i2}xi2和xj1x_{j1}xj1之间连边,流量为1
然后将s1s_1s1和所有的xi1x_{i1}xi1连起来,流量为1,表示每个点都有可能成为某个子序列的开头
同时将ttt和所有的xi2x_{i2}xi2连起来
从s0s_0s0到ttt跑一次最小费用最大流并且取反就是答案了
PS:出题人卡掉了SPFA的板子。。
我的做法是每次往前连边的时候暴力维护KKK个最大值,如果当前的点小于这KKK个最大值就不要连边了(哦显然这个做法应该是假的,不过主要目的还是为了边连的少一点)
听说有人暴力连K2K^2K2条边都过了。。。
这题适合乱搞
#include <bits/stdc++.h>
using namespace std;
const int maxn=6e3+5;
typedef long long ll;
struct Edge
{
int from,to,cap,flow,cost;
Edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
const int INF=2e8;
vector <Edge> edge;
vector <int> g[maxn];
int a[maxn],p[maxn],c[maxn],inq[maxn];
void init(int n)
{
edge.clear();
for (int i=0;i<=n;i++)
g[i].clear();
}
void addedge(int from,int to,int cap,int cost)
{
edge.push_back(Edge{from,to,cap,0,cost});
edge.push_back(Edge{to,from,0,0,-cost});
int m=edge.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
bool bellmanford(int s,int t,int limit_flow,int &flow,ll &cost)
{
memset(a,0,sizeof(a));
memset(c,0x3f,sizeof(c));
memset(inq,0,sizeof(inq));
queue<int> q;
a[s]=INF;
c[s]=0;
inq[s]=1;
q.push(s);
while (!q.empty())
{
int x=q.front();
inq[x]=0;
q.pop();
for (int i=0;i<g[x].size();i++)
{
Edge &e=edge[g[x][i]];
if (e.cap>e.flow && c[e.to]>c[x]+e.cost)
{
c[e.to]=c[x]+e.cost;
p[e.to]=g[x][i];
a[e.to]=min(a[x],e.cap-e.flow);
if (!inq[e.to])
{
q.push(e.to);
inq[e.to]=1;
}
}
}
}
if (c[t]>=INF) return false;
if (a[t]+flow>limit_flow) a[t]=limit_flow-flow;
for (int i=t;i!=s;i=edge[p[i]].from)
{
edge[p[i]].flow+=a[t];
edge[p[i]^1].flow-=a[t];
}
flow+=a[t];
cost+=(ll)c[t]*a[t];
return true;
}
int MCMF(int s,int t,int limit_flow,ll &cost)
{
int flow=0;
cost=0;
while (flow<limit_flow && bellmanford(s,t,limit_flow,flow,cost));
return flow;
}
void build(int n,int k)
{
init(2*n+3);
int ss=0,s=2*n+1,t=2*n+2;
for (int j=1;j<=n;j++)
{
int cnt=0;
priority_queue<int,vector<int>,less<int> > q;//神奇的精髓部分。。。
for (int i=j-1;i>=1;i--)
if (a[i]<=a[j])
{
if (cnt <= k) {addedge(2*i,2*j-1,1,0); q.push(a[i]); cnt++;}
else
if (a[i] > q.top())
{
addedge(2*i,2*j-1,1,0);
q.push(a[i]);
q.pop();
}
}
}
for (int i=1;i<=n;i++)
addedge(2*i-1,2*i,1,-a[i]);
for (int i=1;i<=n;i++)
addedge(s,2*i-1,1,0);
addedge(ss,s,k,0);
for (int i=1;i<=n;i++)
addedge(2*i,t,1,0);
ll cost;
MCMF(ss,t,INF,cost);
printf("%lld\n",-cost);
}
int main()
{
//freopen("1.in","r",stdin);
int t,n,k;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&k);
//cout<<n<<' '<<k<<endl;
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(n,k);
}
return 0;
}