• 口胡(然而有代码)


    把 AFO 之前口胡的题放这里,一句话题解。

    题目计数:19。

    (1.) P4113 [HEOI2012]采花

    和 HH的项链一样,维护一下第二个出现的值,然后差分,树状数组即可,时间复杂度 (mathcal O(nlog n))


    (2.) P2398 GCD SUM

    有公式:

    [sumlimits_{d|n}varphi(d)=n ]

    推式子:

    [egin{aligned}sumlimits_{i=1}^nsumlimits_{j=1}^ngcd(i,j)&=sumlimits_{i=1}^nsumlimits_{j=1}^nsum_{d|gcd(i,j)}varphi(d)cr&=sumlimits_{d=1}^nvarphi(d)leftlfloordfrac{n}{d} ight floor^2end{aligned} ]

    整除分块,时间复杂度 (mathcal O(n+sqrt{n}))


    (3.) P1314 聪明的质监员

    发现 (y_i) 是单调的,求完和还是单调的。

    那么二分一个 (W),对于序列每次维护一个前缀和,对每个区间差分一下,就能 (mathcal O(1)) 了,时间复杂度 (mathcal O((n+m)log w_i))


    (4.) P6625 [省选联考 2020 B 卷] 卡牌游戏

    省选的橙题也是挺有意思的.jpg


    首先,题解中的做法我并没有想到。

    但着不代表这题不可做,,,

    我们考虑什么时候收手合并,当然是负数了!
    如果负数后面有个很大的数呢?
    如果费力把后面的数纳入现在的卡,那么就会少一次合并机会,得不偿失,所以只要是非负数就合并为一个卡,同时更新 (sum)

    记录一下合并完的卡片,作为第一张,第二张用一个动态的指针来记录一下,记得判断是否已经到了最后一张卡片,如果是的话并且总和比 (0) 大,就加上,还有就是要注意 long long

    时间复杂度是 (mathcal O(n))

    (Code):

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 100005 
    #define ll long long
    
    int n,a[MAXN];
    ll sum=0,fir=0;
    ll now=0;
    int lst=2;
    
    int main()
    {
    	read(n);for(int i=1;i<=n;i++) read(a[i]);
    	fir=a[1];
    	while(lst<=n)
    	{
    		now=fir+(ll)a[lst];
    		if(lst==n&&now>=0) sum+=now;
    		lst++;
    		int k=lst;
    		for(int i=k;i<=n;i++)
    		{
    			if(now>=0){sum+=now,fir=now;break;}
    			now+=a[i],lst++;
    			if(i==n&&now>=0) sum+=now;
    		}
    	}
    	printf("%lld
    ",sum);
    	return 0;
    }
    

    (5.) P2285 [HNOI2004]打鼹鼠

    真就 (mathcal O(m^2)) 啊?

    (dp) 似乎很显然,并没有蓝题难度。

    (dp_i) 为最后敲的鼹鼠为第 (i) 只鼹鼠时最多的得分,那么:

    [dp_i=maxlimits_{j=1}^{i-1} {(dp_j+1)×[|x_{a_i}-x_{a_j}|+|y_{a_i}-y_{a_j}|leqslant mathit{Delta} t]} ]

    然后得到所有 (dp) 数组中的最大值即可,注意 (dp_1=1) ,假设第一个一定能用。

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    using namespace std;
    
    #define MAXN 10005
    
    int n,m;
    struct node
    {
    	int t,x,y;
    }a[MAXN];
    int dp[MAXN]={0},ans=1;
    
    inline int read()
    {
    	int x=0;
    	char c=getchar();
    	while(c>'9'||c<'0'){c=getchar();}
    	while(c<='9'&&c>='0'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    	return x;
    }
    
    inline int dis(int l,int r){return abs(a[l].x-a[r].x)+abs(a[l].y-a[r].y);}
    
    int main()
    {
    	n=read(),m=read();
    	for(register int i=1;i<=m;++i) a[i].t=read(),a[i].x=read(),a[i].y=read();
    	dp[1]=1;
    	for(register int i=2;i<=m;++i)
    	{
    		for(register int j=m-1;j>=1;--j)
    		{
    			if(a[i].t-a[j].t>=dis(i,j)) dp[i]=max(dp[i],dp[j]+1),ans=max(ans,dp[i]);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    (C++ 14+O_2) 就真快啊


    (6.) UVA11572 唯一的雪花 Unique Snowflakes

    紫书上给出了 (mathcal O(nlog n)) 的做法,所以我给出了写法比较麻烦一些的 (mathcal O(n)) 做法。

    考虑对每个点维护下一个出现此颜色的位置,并在每个位置记录有是否有前面的点把他标记了。

    最后维护一个指针标记一下,如果这个点和他下一个都在一个暂时的最小区间中,就更新最小区间,并不断处理扫过区间的弹出。

    这样就能 (mathcal O(n)) 了。


    (7.) P2508 [HAOI2008]圆上的整点

    详解见:Link

    珂以看视频解决。


    (8.) P1967 货车运输

    看标签大法好。

    求出最大生成树,所有的最优方案都包含在这里面。

    然后维护两点之间路径的最小值,显然可以用树剖 (+) 线段树或 ST 表维护,但是不会 ST 表了,只能写线段树了/kk。

    复杂度 (mathcal O(qlog ^2 n))

    代码:

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"algorithm"
    #include"cstring"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 50005
    #define inf 2147483647
    
    int n,m,q;
    struct node
    {
        int x,y,z;
    }ed[MAXN];
    struct edge
    {
        int to,nxt,w;
    }e[MAXN<<1];
    int head[MAXN],cnt=0,c=0;
    int f[MAXN],fat[MAXN];
    int tot[MAXN],son[MAXN];
    int t[MAXN],vis[MAXN];
    int cntt=0,topf[MAXN],id[MAXN];
    int dis[MAXN],dep[MAXN];
    struct Tree
    {
        int maxn,l,r;
    }a[MAXN<<2];
    int x,y;
    
    inline void update(int k){a[k].maxn=min(a[k<<1].maxn,a[k<<1|1].maxn);}
    
    void build(int k,int l,int r)
    {
        a[k].l=l,a[k].r=r;
        if(l==r){a[k].maxn=t[l];return;}
        int mid=(l+r)>>1;
        build(k<<1,l,mid),build(k<<1|1,mid+1,r);
        update(k);
        return;
    }
    
    int query(int k,int l,int r)
    {
        if(l>r) return inf;
        if(a[k].l==l&&a[k].r==r) return a[k].maxn;
        int mid=(a[k].l+a[k].r)>>1;
        if(r<=mid) return query(k<<1,l,r);
        else if(l>mid) return query(k<<1|1,l,r);
        else return min(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
    }
    
    void add(int u,int v,int w)
    {
        e[++cnt].to=v;
        e[cnt].w=w;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    
    void init(int n){for(int i=1;i<=n;i++) f[i]=i;}
    int getf(int u){return f[u]=(f[u]==u)?u:getf(f[u]);}
    bool merge(int u,int v){int t1=getf(u),t2=getf(v);if(t1!=t2){f[t2]=t1;return true;}else return false;}
    
    bool cmp(node n,node m){return n.z>m.z;}
    
    int dfs1(int cur,int fa,int deep)
    {
        vis[cur]=1;
        fat[cur]=fa;
        tot[cur]=1;
        dep[cur]=deep;
        int maxson=0;
        for(int i=head[cur];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(j==fa) continue;
            tot[cur]+=dfs1(j,cur,deep+1);
            if(maxson<tot[j]) maxson=tot[j],son[cur]=j,dis[cur]=e[i].w;
        }
        return tot[cur];
    }
    
    void dfs2(int cur,int fa,int top,int lng)
    {
    	vis[cur]=1;
        topf[cur]=top;
        if(fa!=cur) cntt++,id[cur]=cntt,t[cntt]=lng;
        else cntt++,id[cur]=cntt,t[cntt]=inf; 
    	if(!son[cur]) return;
        dfs2(son[cur],cur,top,dis[cur]);
        for(int i=head[cur];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(!vis[j]) dfs2(j,cur,j,e[i].w); 
        }
        return;
    }
    
    int work(int x,int y)
    {
        if(getf(x)!=getf(y)) return -1;
        int ans=inf;
        while(topf[x]!=topf[y])
        { 
    		if(dep[topf[x]]<=dep[topf[y]]) swap(x,y);
    		ans=min(ans,query(1,id[topf[x]],id[x]));
            x=fat[topf[x]];
        }
        if(dep[x]>dep[y]) swap(x,y);
        if(x==y) return ans;
        else return ans=min(ans,query(1,id[son[x]],id[y]));
    }
    
    int main()
    {
        read(n),read(m);
        for(int i=1;i<=m;i++) read(ed[i].x),read(ed[i].y),read(ed[i].z);
        sort(ed+1,ed+1+m,cmp),init(n);
        for(int i=1;i<=m;i++)
        {
            if(merge(ed[i].x,ed[i].y))
            {
                c++;
                add(ed[i].x,ed[i].y,ed[i].z),add(ed[i].y,ed[i].x,ed[i].z);
                if(c==n-1) break;
            }
        }
        for(int i=1;i<=n;i++) if(!vis[i]) fat[i]=i,dfs1(i,0,1);
        memset(vis,0,sizeof(vis)); 
    	for(int i=1;i<=n;i++) if(!vis[i]) dfs2(i,i,i,0);
        build(1,1,cntt);
        read(q);
        while(q--)
        {
            read(x),read(y);
            printf("%d
    ",work(x,y));
        }
        return 0;
    }
    

    (9.) P1038 神经网络

    直接求一下深度,然后枚举每个深度的每一个点暴力更新即可。

    怎么我写的这么长啊,怎么数据范围这么迷啊,怎么题解都用的拓扑排序啊。

    时间复杂度 (mathcal O(nlog n+p))

    #include"iostream"
    #include"cstdio"
    #include"cstring"
    #include"cmath"
    #include"algorithm"
    using namespace std;
    
    #define MAXN 105
    #define read(x) scanf("%d",&x)
    
    int n,m;
    int c[MAXN],u[MAXN];
    struct edge
    {
    	int to,nxt,w;
    }e[MAXN*MAXN];
    int head[MAXN],cnt=0;
    int x,y,z;
    struct node
    {
    	int id,dep;
    }a[MAXN];
    struct ans
    {
    	int id,val;
    }b[MAXN];
    int cntt=0;
    int vis[MAXN],cc=0;
    int beg[MAXN],mch[MAXN];
    int maxd=0;
    int flg=0;
    
    bool cmp1(node n,node m){return n.dep<m.dep;}
    
    bool cmp2(ans n,ans m){return n.id<m.id;}
    
    void add(int u,int v,int c)
    {
    	e[++cnt].to=v;
    	e[cnt].nxt=head[u];
    	e[cnt].w=c;
    	head[u]=cnt;
    }
    
    void dfs(int cur,int de)
    {
    	a[++cc].id=cur,a[cc].dep=de,maxd=max(maxd,de);
    	vis[cur]=1;
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(!vis[j]) dfs(j,de+1);
    	}
    	return;
    }
    
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(c[i]),read(u[i]);
    	for(int i=1;i<=m;i++) read(x),read(y),read(z),add(x,y,z);
    	for(int i=1;i<=n;i++) if(c[i]) dfs(i,1);
    	sort(a+1,a+cc+1,cmp1);
    	for(int i=1;i<=cc;i++){if(!beg[a[i].dep]) beg[a[i].dep]=i;mch[a[i].dep]++;}
    	for(int i=1;i<=mch[1];i++) 
    	{
    		int k=a[i].id;
    		if(c[k]<=0) continue;
    		for(int s=head[k];s;s=e[s].nxt)
    		{
    			int j=e[s].to;
    			c[j]+=e[s].w*c[k];
    		}
    	}
    	for(int i=2;i<=maxd;i++)
    	{
    		for(int j=0;j<mch[i];j++)
    		{
    			int k=a[beg[i]+j].id;
    			c[k]-=u[k];
    			if(c[k]<=0) continue;
    			for(int s=head[k];s;s=e[s].nxt)
    			{
    				c[e[s].to]+=e[s].w*c[k];
    			}
    		}
    	}
    	for(int i=beg[maxd];i<=cc;i++)
    	{
    		if(c[i]>0) b[++cntt].id=a[i].id,b[cntt].val=c[a[i].id];
    	}
    	if(!cntt){printf("NULL
    ");return 0;}
    	sort(b+1,b+cntt+1,cmp2);
    	for(int i=1;i<=cntt;i++) printf("%d %d
    ",b[i].id,b[i].val);
    	return 0;
    }
    

    我这么困,竟然还能 1A???


    (10.) P6477 [NOI Online #2 提高组]子序列问题(民间数据)

    颜色不同性问题,考虑枚举每一个点的贡献,这也是多次枚举中减少枚举唯独的常用技巧。

    可是考试的时候不会啊......

    套路的维护一个前面最后一个出现的 (pos) 数组。

    然后考虑把 (f(i,i))(f(i,n)) 的贡献都加上。如果有新的贡献的话,再加上。

    比如从前有 (3) 个颜色,现在加一个,那么最后加上的贡献为 (16-9=7=2*3+1)

    于是我们维护序列代表从 (i) 发起的序列现在有几种颜色了。

    我们需要增加贡献的是出现新颜色的区间,因为其他区间后面已默认没有出现新颜色加上贡献了,然后把出现新颜色的区间颜色数都加一。

    不懂?举个栗子。

    序列: (1,2,3,1)

    我们只考虑从 (1) 号数发起的区间的贡献。

    在第一个数时,有 (0) 个颜色,贡献 (+1*4)(有四个由这里发起的子序列)。

    在第二个数时,有 (1) 个颜色,贡献 (+3*3)

    在第三个数时,有 (2) 颜色,贡献 (+5*2)

    在第四个数时,因为 前面更新时在最后一个数的贡献已经是 (9) 所以不用加了。

    我们用线段树来维护所有的起点的子序列,复杂度是 (mathcal O(nlog n))

    跑得慢啊

    #include"iostream"
    #include"cstdio"
    #include"algorithm"
    #include"cstring"
    using namespace std;
    
    #define MAXN 1000005 
    #define MOD 1000000007
    #define ll long long
    
    int n,num[MAXN];
    struct node
    {
    	int id,val;
    }b[MAXN];
    int lst=0,cnt=0;
    int col[MAXN],pos[MAXN];
    ll ans=0;
    
    inline int read()
    {
    	int x=0;
    	char c=getchar();
    	while(c<'0'||c>'9'){c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    	return x;
    }
    
    inline bool cmp(node n,node m){return n.val<m.val;}
    
    inline void hsh(int n)
    {
    	sort(b+1,b+n+1,cmp);
    	for(register int i=1;i<=n;i++)
    	{
    		if(b[i].val!=lst) lst=b[i].val,++cnt;
    		num[b[i].id]=cnt;
    	}
    	return;
    }
    
    struct Tree
    {
    	int l,r;
    	ll sum,lazy;
    }a[MAXN<<2];
    
    inline void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum;}
    
    inline void build(int k,int l,int r)
    {
    	a[k].l=l,a[k].r=r;
    	if(l==r){a[k].sum=0;return;}
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    	update(k);return;
    }
    
    inline void lazydown(int k)
    {
    	if(a[k].l==a[k].r){a[k].lazy=0;return;}
    	a[k<<1].sum+=(a[k<<1].r-a[k<<1].l+1)*a[k].lazy;
    	a[k<<1|1].sum+=(a[k<<1|1].r-a[k<<1|1].l+1)*a[k].lazy;
    	a[k<<1].lazy+=a[k].lazy;
    	a[k<<1|1].lazy+=a[k].lazy;
    	a[k].lazy=0;
    	return;
    }
    
    inline void turn(int k,int l,int r)
    {
    	if(a[k].lazy) lazydown(k);
    	if(a[k].l==l&&a[k].r==r)
    	{
    		a[k].sum+=(ll)(a[k].r-a[k].l+1);
    		a[k].lazy++;
    		return;
    	}
    	int mid=(a[k].l+a[k].r)>>1;
    	if(r<=mid) turn(k<<1,l,r);
    	else if(l>mid) turn(k<<1|1,l,r);
    	else turn(k<<1,l,mid),turn(k<<1|1,mid+1,r);
    	update(k);
    }
    
    inline ll query(int k,int l,int r)
    {
    	if(a[k].lazy) lazydown(k);
    	if(a[k].l==l&&r==a[k].r) return a[k].sum;
    	int mid=(a[k].l+a[k].r)>>1;
    	if(r<=mid) return query(k<<1,l,r);
    	else if(l>mid) return query(k<<1|1,l,r);
    	else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
    }
    
    int main()
    {
    	n=read();
    	for(register int i=1;i<=n;i++) b[i].val=read(),b[i].id=i;	
    	hsh(n);
    	for(register int i=1;i<=n;i++) pos[i]=col[num[i]],col[num[i]]=i;
    	build(1,1,n);
    	for(register int i=1;i<=n;i++)
    	{
    		ll now=query(1,pos[i]+1,i);
    		now=now*2+i-pos[i];
    		ans=(ans%MOD+(n-i+1)%MOD*now%MOD)%MOD;
    		turn(1,pos[i]+1,i);
    	}
    	printf("%lld
    ",ans%MOD);
    	return 0;
    }//开O2才能过吧
    

    (11.) P1031 均分纸牌

    挺妙的,其实就是从左到右扫一遍,如果从上一个起点到这里正好是可以均分的,那么下一个是一个新起点,这里的最后一个与下一块不沟通,所以操作次数从 (n) 中减一。

    时间复杂度是 (mathcal O(n))

    #include"iostream"
    #include"cstdio"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 105
    
    int n,a[MAXN];
    int sum=0,av=0;
    int ans;
    
    int main()
    {
    	read(n);
    	ans=n;
    	for(int i=1;i<=n;i++) read(a[i]),av+=a[i];
    	av/=n;
    	for(int i=1;i<=n;i++)
    	{
    		sum+=a[i];
    		if(sum==av*i) ans--;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    (12.) P4905 报纸

    暴力暴力暴力

    把往下和往右作为主导,然后暴力判断每一个格的 (gcd),然后贪心的选就好了。

    复杂度是 (mathcal O(n^2log n)),并没有蓝题难度。


    (13.) P1896 [SCOI2005]互不侵犯

    (01) 串代表是否被选,然后判断每一个状态是否可行。

    可以开三维数组代表状态,共选几个位置,行数。

    这里用一种方式压掉了行数这一维(难道这就是滚动数组?算了算了,状压 dp 我都没正经学过还 A 了这题呢),然而大大增加了码量。

    然后空间复杂度应该是 (mathcal O(2^nk)),时间复杂度应该是 (mathcal O(2^{2n}n(n+k)))

    #include"iostream"
    #include"cstdio"
    #include"cstring"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN (1<<9)+5
    #define ll long long
    
    int n,k;
    ll a[MAXN][85],b[MAXN][85];
    bool flag[10],mark=false;
    int cnt=0;
    ll ans=0;
    
    int main()
    {
    	read(n),read(k);
    	memset(a,0,sizeof(a));
    	memset(b,0,sizeof(b));
    	for(int s=0;s<(1<<n);s++)
    	{
    		mark=false,cnt=0;
    		for(int i=0;i<n;i++){if((1<<i)&s) flag[i]=1;else flag[i]=0;}
    		{
    			if(flag[i]&&flag[i-1]) mark=true;
    			if(flag[i]) cnt++;
    		}
    		if(flag[0]) cnt++;
    		if(mark) a[s][cnt]=0;
    		else a[s][cnt]=1ll;
    	}
    	for(int qwq=2;qwq<=n;qwq++)
    	{
    		if(qwq%2==0)
    		{
    			memset(b,0,sizeof(b));
    			for(int s1=0;s1<(1<<n);s1++)
    			{
    				for(int s2=0;s2<(1<<n);s2++)
    				{
    					mark=false,cnt=0;
    					for(int i=0;i<n;i++)
    					{
    						if(s2&(1<<i))
    						{
    							if(i>=1&&(s1&(1<<(i-1)))) mark=true;
    							if(s1&(1<<i)) mark=true;
    							if(i<n&&(s1&(1<<(i+1)))) mark=true;
    						}
    						if(mark) break;
    					}
    					for(int i=1;i<n;i++) if(((1<<i)&s2)&&((1<<(i-1))&s2)) mark=true;
    					if(mark) continue;
    					for(int i=0;i<n;i++) if(s2&(1<<i)) cnt++;
    					for(int i=cnt;i<=k;i++) b[s2][i]+=a[s1][i-cnt];
    				}
    			}
    
    		}
    		else 
    		{
    			memset(a,0,sizeof(a));
    			for(int s1=0;s1<(1<<n);s1++)
    			{
    				for(int s2=0;s2<(1<<n);s2++)
    				{
    					mark=false,cnt=0;
    					for(int i=0;i<n;i++)
    					{
    						if(s2&(1<<i))
    						{
    							if(i>=1&&(s1&(1<<(i-1)))) mark=true;
    							if(s1&(1<<i)) mark=true;
    							if(i<n&&(s1&(1<<(i+1)))) mark=true;
    						}
    						if(mark) break;
    					}
    					for(int i=1;i<n;i++) if(((1<<i)&s2)&&((1<<(i-1))&s2)) mark=true;
    					if(mark) continue;
    					for(int i=0;i<n;i++) if(s2&(1<<i)) cnt++;
    					for(int i=cnt;i<=k;i++) a[s2][i]+=b[s1][i-cnt];
    				}
    			}
    		}
    	}
    	if(n%2==0) for(int s=0;s<(1<<n);s++) ans+=b[s][k];
    	else for(int s=0;s<(1<<n);s++) ans+=a[s][k];
    	printf("%lld
    ",ans);
    }
    

    (14.) P4071 [SDOI2016]排列计数

    错排问题。

    递推式 :

    [D_n=(n-1)(D_{n-1}+D_{n-2}) ]

    冷静分析之后发现答案是:

    [dbinom{n}{m} D_{n-m} ]

    复杂度是 (mathcal O(n+Tlog p))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 1000005
    #define MOD 1000000007
    
    int t,n,m;
    int d[MAXN],jc[MAXN];
    
    void get()
    {
    	d[0]=1,jc[0]=1;
    	d[1]=0,d[2]=1;
    	jc[1]=1,jc[2]=2;
    	for(int i=3;i<=1000000;i++)
    	{
    		d[i]=1ll*(i-1)*(d[i-1]+d[i-2])%MOD;
    		jc[i]=1ll*jc[i-1]*i%MOD;
    	}
    	return;
    }
    
    int quickpow(int n,int k)
    {
    	int ans=1,base=n;
    	while(k)
    	{
    		if(k&1) ans=1ll*ans*base%MOD;
    		k>>=1;
    		base=1ll*base*base%MOD;
    	}
    	return ans%MOD;
    }
    
    int inv(int a){return quickpow(a,MOD-2);}
    
    int com(int n,int m){return 1ll*jc[n]*inv(1ll*jc[m]*jc[n-m]%MOD)%MOD;}
    
    int main()
    {
    	get();
    	read(t);
    	while(t--)
    	{
    		read(n),read(m);
    		printf("%d
    ",(int)(1ll*com(n,m)*d[n-m]%MOD));
    	}
    	return 0;
    } 
    

    (15.) P4513 小白逛公园

    这不是线段树最大子段和板子题吗?

    只是加了个单点修改,反正不是区间修改,就能搞。

    时间复杂度 (mathcal O(mlog n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 500005
    
    int n,m;
    int t[MAXN],type,l,r;
    struct Tree
    {
    	int l,r,pre,suf;
    	int sum,maxn;
    	Tree(){l=r=pre=suf=sum=maxn=0;}
    }a[MAXN<<2];
    
    inline void update(int k)
    {
    	a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
    	a[k].pre=max(a[k<<1].pre,a[k<<1].sum+a[k<<1|1].pre);
    	a[k].suf=max(a[k<<1|1].suf,a[k<<1|1].sum+a[k<<1].suf);
    	a[k].maxn=max(a[k<<1].suf+a[k<<1|1].pre,max(a[k].pre,max(a[k<<1].maxn,max(a[k<<1|1].maxn,a[k].suf))));	
    }
    
    void build(int k,int l,int r)
    {
    	a[k].l=l,a[k].r=r;
    	if(l==r){a[k].sum=a[k].pre=a[k].suf=a[k].maxn=t[l];return;}
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    	update(k);
    }
    
    void turn(int k,int x,int y)
    {
    	if(a[k].l==a[k].r){a[k].sum=a[k].pre=a[k].suf=a[k].maxn=y;return;}
    	int mid=(a[k].l+a[k].r)>>1;
    	if(x<=mid) turn(k<<1,x,y);
    	else turn(k<<1|1,x,y);
    	update(k);
    }
    
    Tree query(int k,int l,int r)
    {
    	if(a[k].l==l&&a[k].r==r) return a[k];
    	int mid=(a[k].l+a[k].r)>>1;
    	if(r<=mid) return query(k<<1,l,r);
    	else if(l>mid) return query(k<<1|1,l,r);
    	else
    	{
    		Tree ans,g=query(k<<1,l,mid),h=query(k<<1|1,mid+1,r);
    		ans.pre=max(g.pre,g.sum+h.pre);
    		ans.suf=max(h.suf,h.sum+g.suf);
    		ans.sum=g.sum+h.sum;
    		ans.maxn=max(g.maxn,max(h.maxn,max(ans.pre,max(ans.suf,g.suf+h.pre))));
    		return ans;
    	}
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(t[i]);
    	build(1,1,n);
    	for(int i=1;i<=m;i++)
    	{
    		read(type),read(l),read(r);
    		if(type==1&&l>r) swap(l,r);
    		if(type==1) printf("%d
    ",query(1,l,r).maxn);
    		else turn(1,l,r);
    	}
    	return 0;
    }
    

    (16.) P1483 序列变换

    这个操作二这么少就不正常。

    考虑每一次操作二什么时候被影响,其实是当被操作一中 (x) 整除的时候。

    那就对每一个 (x) 打标记,然后操作二中看他有多少因数。

    设有 (m_1) 个操作一, (m_2) 个操作二,那么复杂度是 (mathcal O(m_1+m_2sqrt{x}))

    #include"iostream"
    #include"cstdio"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 1000005
    
    int n,m;
    int a[MAXN],c[MAXN];
    int t,x,y;
    
    inline int dfs(int n)
    {
    	int ans=0;
    	for(int i=1;i*i<=n;i++)
    	{
    		if(n%i==0)
    		{
    			if(i*i==n) ans+=c[i];
    			else ans=ans+c[i]+c[n/i]; 
    		}
    	}
    	return ans;
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(a[i]);
    	for(int i=1;i<=m;i++)
    	{
    		read(t),read(x);
    		if(t==1) read(y),c[x]+=y;
    		else printf("%d
    ",a[x]+dfs(x));	
    	} 
    	return 0;
    }
    

    (17.) P1533 可怜的狗狗

    主席树板子。

    能卡 (21) 倍空间,(21) 倍空间 (WA),更少 (RE)

    代码不好看,不贴了。

    时间复杂度 (mathcal O((n+m)log n))


    (18.) P2704 [NOI2001]炮兵阵地

    又是一道状压 (dp)

    考虑用 (dp[s1][s2][k]) 代表枚举到第 (k) 行得到的上一行状态为 (s_1),上上行状态为 (s_2) 的最大方案数。

    然后暴力判是否合法即可。

    这样空间复杂度是 (mathcal O(2^{2n}m)),时间复杂度是 $$。

    考虑像 互不侵犯 那题一样写一个 假的 滚动数组,空间复杂度就是 (mathcal O(2^{2n})) 了。

    时间稍微卡一些能过(确信)。

    看了看题解,发现题解的方法真妙啊。

    我们可以对每一行先排除掉一些不合法的答案,然后存起来,直接枚举这些合法的就好了。


    (19.) P2184 贪婪大陆

    不会吧不会吧,就我一个人写的平衡树吗?<-太菜了,看不出树状数组

    我们考虑每个询问前面的区间对此询问的贡献。

    显然,只有两个区间有交集,才能对答案造成贡献。

    然而交集不好维护,思考从反面解决问题,即哪些区间对答案没有贡献。

    显然修改区间 ([l,r]) 对询问区间 ([L,R]) 没有贡献,当且仅当 (l>R)(r<L)。由于 (lleq r),则两种情况不可能同时满足。

    其实有点像偏序问题,但我们不向那方面想试试。

    那么我们维护两颗平衡树,分别代表修改的左右区间的集合,然后每次询问询问区间的左右端点的排名即可。

    这里是用替罪羊树实现的,复杂度是 (mathcal O(mlog m))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"algorithm"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 100005
    #define alpha 0.7666
    
    int n,m;
    int t,l,r;
    int sum=0;
    struct Tree
    {
    	int root,cnt,c;
    	int ls[MAXN],rs[MAXN];
    	int wn[MAXN],sh[MAXN];
    	int val[MAXN];
    	int rt[MAXN];
    	
    	bool judge(int k){return wn[k]&&(double)sh[k]*alpha<(double)max(sh[ls[k]],sh[rs[k]]);}
    	
    	void update(int k){sh[k]=sh[ls[k]]+sh[rs[k]]+wn[k];}
    	
    	void unfold(int k)
    	{
    		if(!k) return;
    		unfold(ls[k]);
    		if(wn[k]) rt[++c]=k;
    		unfold(rs[k]);
    	}
    	
    	int build(int l,int r)
    	{
    		if(l>=r) return 0;
    		int mid=(l+r)>>1;
    		ls[rt[mid]]=build(l,mid);
    		rs[rt[mid]]=build(mid+1,r);
    		update(rt[mid]);
    		return rt[mid];
    	}
    	
    	void bal(int &k){c=0;unfold(k);k=build(1,c+1);}
    	
    	void insert(int &k,int v)
    	{
    		if(!k)
    		{
    			k=++cnt;
    			if(!root) root=1;
    			wn[k]=sh[k]=1,val[k]=v;
    			ls[k]=rs[k]=0;
    			return;
    		}
    		else
    		{
    			if(val[k]==v) wn[k]++;
    			else if(val[k]>v) insert(ls[k],v);
    			else insert(rs[k],v);
    			update(k);
    			if(judge(k)) bal(k);
    		}
    		return;
    	}
    	
    	int rkup(int k,int v)
    	{
    		if(!k) return 0;
    		if(val[k]==v) return sh[ls[k]];
    		else if(val[k]>v) return rkup(ls[k],v);
    		else return wn[k]+sh[ls[k]]+rkup(rs[k],v);
    	}
    	
    	int rkdown(int k,int v)
    	{
    		if(!k) return 0;
    		if(val[k]==v) return sh[ls[k]]+wn[k];
    		else if(val[k]>v) return rkdown(ls[k],v);
    		else return wn[k]+sh[ls[k]]+rkdown(rs[k],v);
    	}
    }Tl,Tr;
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=m;i++)
    	{
    		read(t),read(l),read(r);
    		if(t==1)
    		{
    			Tl.insert(Tl.root,r);
    			Tr.insert(Tr.root,l);
    			sum++;
    		}
    		else
    		{
    			int ans=sum;
    			int now=Tl.rkup(Tl.root,l);
    			ans-=now;
    			now=Tr.rkdown(Tr.root,r);
    			now=sum-now;
    			ans-=now;
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    HDFS命令操作和高可用
    nginx-nginx和反向代理概念
    day01 Hadoop 简单介绍及架构设计
    常用正则表达式
    【JavaWeb笔记】第二章 JDBC
    【JavaSE笔记】第二章 进制数据类型和运算符
    LeetCode-94 Binary Tree Inorder Traversal Solution (with Java)
    LeetCode-1019 Next Greater Node In Linked List Solution (with Java)
    LeetCode-946 Validate Stack Sequences Solution (with Java)
    LeetCode-739 Daily Temperatures Solution (with Java)
  • 原文地址:https://www.cnblogs.com/tlx-blog/p/13168415.html
Copyright © 2020-2023  润新知