cqoi2015部分题解

27 篇文章 0 订阅
12 篇文章 0 订阅

只做了前三题。。。。。

T1、选数

先把题目转化为求选n个数最大公约数为1,不用说了。

假定f[i]为选出n个数最大公约数为i的方案数。

由于题目中有条件H-L<=10^5,所以i<=10^5即可。

令L=(L-1)/i, R=H/i

f[i] = (R-L)^n - sigma f[a*i] - (R-L)

最后的R-L为减去全部选择一个数的方案数

答案为f[1]

PS:如果范围中有1,注意f[1]++,因为全部选1也是一种方案

另一种做法,可以直接用莫比乌斯反演,莫比乌斯函数求和可参见我的另一篇博文

这种做法比较卡时,PoPoQQQ大神正是用了种做法

http://blog.csdn.net/popoqqq/article/details/45078079

贴上我的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const int Maxn=100005, Mod=(1e9)+7;
int f[Maxn],L,R,n,k,i,j;
 
int qck(int a,int b){
  int ret = 1;
  for (;b>0;b>>=1){
    if (b&1) ret=(long long)ret*a%Mod;
    a=(long long)a*a%Mod;
  }
  return ret;
}
 
int main(){
  //freopen("number.in","r",stdin);
  //freopen("number.out","w",stdout);
  scanf("%d%d%d%d",&n,&k,&L,&R);
  L = (L-1)/k+1; R = R/k;
  for (i=R-L;i>0;i--){
    f[i] = (qck(R/i-(L-1)/i,n)-(R/i-(L-1)/i)+Mod)%Mod;
    for (j=i+i;j<=R-L;j+=i)
      f[i] = (f[i]-f[j]+Mod)%Mod;
  }
  if (L==1) f[1]++;
  printf("%d\n",f[1]);
  return 0;
}

T2、网络吞吐量

很经典的一道网络流题了,做法可以参见AHOI2006上学路线,不讲了

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
#define pb push_back
typedef long long LL;
const int INF=1e9;
LL flow[300000],Ds[505],Dt[505],minx,ans;
int a[1005],js[1005],c[1005];
int node[300000],next[300000];
int n,m,x,y,z,N,i,j,vj,tot,q[1005],l,r;
bool flag,v[505];
struct edge{ int x,w; };
vector <edge> e[505];
 
void Dijkstra(LL d[],int S){
  for (i=1;i<=n;i++)
    d[i]=INF, d[i]*=d[i];
  memset(v,0,sizeof(v));
  d[S] = 0;
  for (i=1;i<n;i++){
    LL minx = INF; minx*=minx;
    for (j=1;j<=n;j++)
    if (!v[j] && minx>d[j])
      minx = d[j], vj = j;
    v[vj] = 1;
    int len = e[vj].size();
    for (j=0;j<len;j++)
    if (d[e[vj][j].x]>minx+e[vj][j].w)
      d[e[vj][j].x]=minx+e[vj][j].w;
  }
}
 
void add(int x,int y,LL z){
  node[++tot]=y; next[tot]=a[x]; a[x]=tot; flow[tot]=z;
  node[++tot]=x; next[tot]=a[y]; a[y]=tot; flow[tot]=0;
}
 
void init(){
  memset(js,0,sizeof(js));
  memset(c,10,sizeof(c));
  for (q[l=r=1]=N,js[c[N]=0]=1;l<=r;l++)
    for (i=a[q[l]];i;i=next[i])
    if ((i&1) && c[node[i]]>c[q[l]]+1)
      js[ c[ q[++r]=node[i] ]=c[q[l]]+1 ]++;
}
 
void sap(int x){
  if (x==N){
    ans += (LL)minx;
    flag = 1;
    return;
  }
  int minn = minx, e = N-1, i;
  for (i=a[x];i;i=next[i])
  if (flow[i]>0){
    if (c[node[i]]+1==c[x]){
      minx = min(minx, flow[i]);
      sap(node[i]);
      if (c[1]>=N) return;
      if (flag) break; else minx = minn;
    }
    e = min(e,c[node[i]]);
  }
  if (flag) flow[i]-=minx, flow[i^1]+=minx;
  else
  {
    if (--js[c[x]]==0) c[1]=N;
    js[ c[x]=e+1 ]++;
  }
}
 
int main(){
  //freopen("network.in","r",stdin);
  //freopen("network.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&z);
    e[x].pb((edge){y,z});
    e[y].pb((edge){x,z});
  }
  Dijkstra(Ds,1);
  Dijkstra(Dt,n);
  for (i=1,tot=1;i<=n;i++){
    int len = e[i].size();
    for (j=0;j<len;j++)
    if (Ds[i]+Dt[e[i][j].x]+e[i][j].w==Ds[n])
      add(i*2,e[i][j].x*2-1,INF);
  }
  for (i=1;i<=n;i++){
    scanf("%d",&x);
    if (i==1 || i==n)
      add(i*2-1,i*2,(LL)INF*INF);
    else add(i*2-1,i*2,x);
  }
  N = 2*n;
  init();
  while (c[1]<N) { flag=0;minx=INF;sap(1); }
  printf("%lld\n",ans);
  return 0;
}

T3、任务查询系统

似乎把这题做得复杂了,

说一说我的做法吧。

建两棵主席树,一棵插入顺序按左端点从小到大排序,一棵按右端点从小到大排序

每棵内层都都按权值插入节点,记录数量和所有插入节点的权值和。

查询的时候只要找到第一棵中左端点最大的小于他的位置和第二棵树中右端点最大的小于他的位置

两棵树做差分即可。

颓废无比。。。。

#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const int Maxn=100005;
typedef long long LL;
LL lastans,sum2[Maxn*40];
int A,B,C,K,n,m,M,i,x,st,T[Maxn],S[Maxn];
int son[Maxn*40][2],sum1[Maxn*40],w[Maxn];
struct arr{ int s,e,p; }a[Maxn],b[Maxn];
 
bool cmp1(const arr &a,const arr &b)
  { return a.s<b.s; }
 
bool cmp2(const arr &a,const arr &b)
  { return a.e<b.e; }
 
void ins(int &q,int p,int l,int r,int pos){
  q = ++st;
  son[q][0] = son[p][0];
  son[q][1] = son[p][1];
  sum1[q] = sum1[p]+1;
  sum2[q] = sum2[p]+(LL)w[pos];
  if (l==r) return;
  int mid = (l+r)>>1;
  if (pos<=mid) ins(son[q][0],son[p][0],l,mid,pos);
    else ins(son[q][1],son[p][1],mid+1,r,pos);
}
 
LL query(int p,int q,int l,int r,int s){
  if (sum1[p]-sum1[q]+s<=K) return sum2[p]-sum2[q];
  int mid=(l+r)>>1;
  if (l==r) return (LL)(K-s)*w[l];
  LL ret1 = query(son[p][0],son[q][0],l,mid,s), ret2 = 0;
  s += sum1[son[p][0]]-sum1[son[q][0]];
  if (s<K) ret2 = query(son[p][1],son[q][1],mid+1,r,s);
  return ret1+ret2;
}
 
int get1(int x){
  int l = 1, r = m, mid, ret=0;
  while (l<=r){
    mid = (l+r)>>1;
    if (a[mid].s<=x) ret=mid, l=mid+1;
      else r=mid-1;
  }
  return ret;
}
 
int get2(int x){
  int l = 1, r = m, mid, ret=0;
  while (l<=r){
    mid = (l+r)>>1;
    if (b[mid].e<=x) ret=mid, l=mid+1;
      else r=mid-1;
  }
  return ret;
}
 
int main(){
  //freopen("query.in","r",stdin);
  //freopen("query.out","w",stdout);
  scanf("%d%d",&m,&n);
  for (i=1;i<=m;i++){
    scanf("%d%d%d",&a[i].s,&a[i].e,&a[i].p);
    b[i] = a[i]; w[i] = a[i].p;
  }
  sort(w+1,w+m+1);
  M = unique(w+1,w+m+1)-w-1;
 
  sort(a+1,a+m+1,cmp1);
  sort(b+1,b+m+1,cmp2);
  for (i=1;i<=m;i++){
    a[i].p = lower_bound(w+1,w+M+1,a[i].p)-w;
    ins(T[i],T[i-1],1,M,a[i].p);
  }
  for (i=1;i<=m;i++){
    b[i].p = lower_bound(w+1,w+M+1,b[i].p)-w;
    ins(S[i],S[i-1],1,M,b[i].p);
  }
 
  for (i=1,lastans=1;i<=n;i++){
    scanf("%d%d%d%d",&x,&A,&B,&C);
    K = ((LL)A*lastans+(LL)B)%(LL)C+1;
    int t1 = get1(x), t2 = get2(x-1);
    lastans = query(T[t1],S[t2],1,M,0);
    printf("%lld\n",lastans);
  }
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值