六省联考2017

期末考试

Luogu
LOJ
BZOJ
显然我们只需要考虑最后公布成绩的那一天。
枚举这一天,预处理前缀和之后即可\(O(1)\)计算最小代价。
注意倒数第二个Subtask这最优的日期就是\(\min(b_i)\),直接计算即可。(如果套用通法会爆long long,但是开unsigned long long就行了)

#include<cctype>
#include<cstdio>
#include<algorithm>
using i64=unsigned long long;
const int N=100007;const i64 inf=1e18;
i64 t1[N],t2[N],A,B,C;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
i64 cal(i64 a,i64 b){return A<B? a>b? b*A+(a-b)*B:a*A:B*a;}
int main()
{
    int n,m;i64 r1=0,r2=0,r3=0,s1=0,s2=0,s3=0,ans=inf;
    A=read(),B=read(),scanf("%lld",&C),n=read(),m=read();
    for(int i=1,x;i<=n;++i) ++t2[x=read()],s3+=x,++r3;
    for(int i=1,x;i<=m;++i) ++t1[x=read()],s2+=x,++r2;
    for(int i=100000;i;--i)
    {
	s1+=i*t1[i],r1+=t1[i],s2-=i*t1[i],r2-=t1[i],s3-=i*t2[i],r3-=t2[i];
	if(!s1) continue;
	ans=std::min(ans,cal(s1-i*r1,r2*i-s2)+(r3*i-s3)*C);
    }
    printf("%lld",ans);
}

相逢是问候

Luogu
LOJ
BZOJ
给定一个数\(p\),最多进行\(O(\log p)\)\(p\leftarrow\varphi(p)\)即可令\(p=1\)
也就是说一个位置在被修改了\(O(\log p)\)次之后,它的值就不会变了。
那么我们记录一个位置被修改过多少次,每次修改的时候区间内的所有位置单独拿出来暴力计算即可。
预处理快速幂即可\(O(\log^2p)\)进行单点计算。
为了不再访问那些被修改超过\(O(\log p)\)次之后的位置,我们可以使用线段树记录区间修改最小次数,或者利用并查集+树状数组。
时间复杂度为\(O(n\log p(\log p+\log n))\)

#include<cctype>
#include<cstdio>
#include<vector>
#include<algorithm>
const int N=50007,M=10007;
int n,m,p,c,a[N],o[N],t[N],tim[N],fa[N],pw1[30][M],pw2[30][M];std::vector<int>mod;
void inc(int&a,int b){a+=b-p,a+=a>>31&p;}
int pow(int a,int b){return 1ll*pw1[b][a%10000]*pw2[b][a/10000]%mod[b];}
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int phi(int x){int val=x;for(int p=2;p*p<=x;++p) if(!(x%p)) for(val-=val/p;!(x%p);x/=p);return val-(x>1)*val/x;}
int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
void add(int p,int v){for(;p<=n;p+=p&-p)inc(t[p],v);}
int ask(int p){int r=0;for(;p;p^=p&-p)inc(r,t[p]);return r;}
void modify(int p,int t){for(a[p]=o[p]>=mod[t]?o[p]%mod[t]+mod[t]:o[p];t--;)if(a[p]=pow(a[p],t),t&&!a[p])a[p]+=mod[t];}
void update(int l,int r){for(l=find(l);l<=r;l=find(l+1))if(add(l,p-a[l]),modify(l,++tim[l]),add(l,a[l]),tim[l]==(int)mod.size()-1)fa[l]=l+1;}
int main()
{
    n=read(),m=read(),p=read(),c=read(),fa[n+1]=n+1;
    for(int i=1;i<=n;++i) add(fa[i]=i,o[i]=a[i]=read());
    for(int x=p;x^1;x=phi(x)) mod.push_back(x);
    mod.push_back(1),mod.push_back(1);
    for(int i=0;i<(int)mod.size();++i)
    {
	for(int j=pw1[i][0]=1;j<=10000;++j) pw1[i][j]=1ll*pw1[i][j-1]*c%mod[i];
	for(int j=pw2[i][0]=1;j<=10000;++j) pw2[i][j]=1ll*pw2[i][j-1]*pw1[i][10000]%mod[i];
    }
    for(int o,l,r;m;--m) o=read(),l=read(),r=read(),o? printf("%d\n",(ask(r)-ask(l-1)+p)%p):(update(l,r),0);
}

组合数问题

Luogu
LOJ
BZOJ
\(\sum\limits_{i\equiv r\pmod k}{nk\choose i}=[x^r](1+x)^{nk}\bmod(x^k-1)\)
循环卷积快速幂即可。

#include<cstdio>
#include<vector>
using vec=std::vector<int>;
int n,r,p,k;
void inc(int&a,int b){a+=b-p,a+=a>>31&p;}
int mul(int a,int b){return 1ll*a*b%p;}
vec operator*(const vec&f,const vec&g)
{
    vec h(k);
    for(int i=0;i<k;++i) for(int j=0;j<k;++j) inc(h[(i+j)%k],mul(f[i],g[j]));
    return h;
}
int main()
{
    scanf("%d%d%d%d",&n,&p,&k,&r);vec E(k),I(k);
    I[0]=1,k==1? E[0]=2%p:E[0]=E[1]=1;
    for(long long e=1ll*n*k;e;e>>=1,E=E*E) if(e&1) I=E*I;
    printf("%d",I[r]);
}

摧毁树状图

Luogu
LOJ
BZOJ
很显然是一个树形dp。
\(f_{u,0}\)表示\(u\)子树内过\(u\)的一条单链。
\(f_{u,1}\)表示\(u\)子树内不经过\(u\)的一条路径。
\(f_{u,2}\)表示\(u\)子树内过\(u\)的一条路径。
\(f_{u,3}\)表示\(u\)子树内的一条路径和过\(u\)的一条单链。
具体的看这个吧Link
或者写个暴力拍,拍出错就加一种情况。
考场上这种旷野大讨论的题一般都不会去做的。

#include<cctype>
#include<cstdio>
#include<vector>
#include<cstring>
const int N=100007;
int ans,f[N][4],deg[N];std::vector<int>e[N];char ibuf[1<<23|1],*iS=ibuf;
int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
void max(int&a,int b){if(b>a)a=b;}
void dfs(int u,int fa)
{
    f[u][0]=f[u][2]=f[u][3]=deg[u],f[u][1]=1;int mx=0;
    for(int v:e[u])
    {
	if(v==fa) continue;
	dfs(v,u);
	max(ans,f[u][3]+f[v][0]-(u==1));
        max(ans,f[u][0]+f[v][3]-(u==1));
        max(ans,f[u][1]+f[v][2]);
        max(ans,f[u][1]+f[v][1]-1);
        max(ans,f[u][2]+f[v][1]-(u==1));
        max(ans,f[u][2]+f[v][2]-(u==1));
        max(f[u][1],f[v][1]);
        max(f[u][1],f[v][2]+1);
        max(f[u][3],f[u][0]+f[v][2]-1);
        max(f[u][3],f[u][2]+f[v][0]-1);
        max(f[u][3],f[v][3]+deg[u]-1);
        max(f[u][3],f[v][0]+mx+deg[u]-2);
        max(f[u][3],f[u][0]+f[v][1]-1);
        max(f[u][2],f[u][0]+f[v][0]-1);
        max(f[u][0],f[v][0]+deg[u]-1);
        max(f[u][2],f[u][0]);
        max(f[u][3],f[u][2]);
        max(mx,f[v][1]);
        max(mx,f[v][2]);
    }
}
int main()
{
    fread(ibuf,1,1<<23,stdin);
    for(int T=read(),o=read(),n;T;--T)
    {
	n=read(),memset(deg+1,0,n<<2);
	for(int i=1;i<=n;++i) e[i].clear();
	for(int i=1;i<=o;++i) read(),read();
	for(int i=2,u,v;i<=n;++i) ++deg[u=read()],++deg[v=read()],--deg[i],e[u].push_back(v),e[v].push_back(u);
	dfs(1,ans=0),printf("%d\n",ans);
    }
}

分手是祝愿

Luogu
LOJ
BZOJ
显然最优的决策是倒序遍历,碰到开着的灯就关上。
然后我们可以枚举约数变枚举倍数在\(O(n\log n)\)的时间复杂度内求出最小操作次数\(cnt\)
\(f_i\)表示从最小操作次数为\(i\)的状态转移到最小操作次数为\(i-1\)的期望操作次数。
转移方程很显然,\(f_i=\frac in+\frac{n-i}n(f_i+f_{i+1}+1)\)\(f_i=\frac{n+(n-i)f_{i+1}}i\)
最后答案就是\(\min(k,cnt)+\sum\limits_{i=k+1}^{cnt}f_i\)

#include<cstdio>
#include<algorithm>
const int P=100003;
int a[P],f[P];
int read(){int x;scanf("%d",&x);return x;}
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int b){int r=1;for(;b;b>>=1,a=mul(a,a))if(b&1)r=mul(a,r);return r;}
int main()
{
    int n=read(),k=read(),cnt=0,ans;f[n+1]=0;
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=n;i;cnt+=a[i--]) for(int j=2*i;j<=n;j+=i) a[i]^=a[j];
    for(int i=n;i;--i) f[i]=mul(mul(n-i,f[i+1])+n,pow(i,P-2));
    ans=std::min(cnt,k);
    for(int i=cnt;i>k;--i) inc(ans,f[i]);
    for(int i=1;i<=n;++i) ans=mul(ans,i);
    printf("%d",ans);
}

寿司餐厅

Luogu
LOJ
BZOJ
首先价格里面的\(cx\)部分可以直接由\(d_{i,i}\leftarrow d_{i,i}-a_i\)完成。
然后我们发现这就是个标准的最大权闭合子图,选\(d_{i,j}\)必须选\(d_{i,j-1},d_{i+1,j}\)
然后考虑价格中\(mx^2\)这部分,建\(\max(a)\)个点,选\(d_{i,i}\)必须选\(-a_i\)即可。

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=107,V=6507,E=60007,inf=1e9;
int a[N],id[N][N],d[N][N],s,t,tot=1,head[V],cur[V],dep[V];struct edge{int v,f,next;}e[E];std::queue<int>q;
int read(){int x;scanf("%d",&x);return x;}
void add(int u,int v,int f){e[++tot]={v,f,head[u]},head[u]=tot,e[++tot]={u,0,head[v]},head[v]=tot;}
int bfs()
{
    memset(dep+1,0x3f,t<<2),memcpy(cur+1,head+1,t<<2),dep[s]=0,q.push(s);
    for(int i,u,v;!q.empty();) for(u=q.front(),q.pop(),i=head[u];i;i=e[i].next) if(dep[v=e[i].v]>inf&&e[i].f) dep[v]=dep[u]+1,q.push(v);
    return dep[t]<inf;
}
int dfs(int u,int lim)
{
    if(!lim||u==t) return lim;
    int v,flow=0;
    for(int&i=cur[u],f;i;i=e[i].next)
        if(dep[v=e[i].v]==dep[u]+1&&(f=dfs(v,std::min(lim,e[i].f))))
        {
            flow+=f,lim-=f,e[i].f-=f,e[i^1].f+=f;
            if(!lim) break;
        }
    return flow;
}
int main()
{
    int n=read(),m=read(),ans=0,cnt=0;s=n*(n+1)/2+1001,t=s+1;
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) d[i][j]=read()-(i==j)*a[i],id[i][j]=++cnt;
    for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) if(i^j) add(id[i][j],id[i+1][j],inf),add(id[i][j],id[i][j-1],inf); else if(m) add(id[i][j],cnt+a[i],inf);
    for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) d[i][j]>0? ans+=d[i][j],add(s,id[i][j],d[i][j]):add(id[i][j],t,-d[i][j]);
    if(m) for(int i=1;i<=1000;++i) add(cnt+i,t,i*i);
    while(bfs()) ans-=dfs(s,inf);
    printf("%d",ans);
}
posted @ 2020-04-19 21:43  Shiina_Mashiro  阅读(161)  评论(0编辑  收藏  举报