zoukankan      html  css  js  c++  java
  • 口胡(然而有代码)<第三章>

    他来了!

    估计是最后一章了/kk。(诶,能去 NOIP 好像可以续命啊)

    上期精彩:口胡(然而有代码)<第二章>

    题目计数:(150)

    (101.) P2659 美丽的序列

    提供一种比较屑的 (mathcal O(nlog n)) 解法。

    当然常数很小。。。。

    首先考虑到将这些数钦定为最小值,然后去找区间,易证答案一定来自于满足此值最小的最长区间。

    我们于是可以从最大值开始枚举,判断他的两侧已经有多少被枚举过了(这些数一定比他大),然后算就完了。

    可是如何判断已经被枚举的最左,最右在哪里呢。

    我们把已被用的搞成区间,记录一下左右端点,然后不断计算,扩展即可得到答案。

    具体见代码:

    #include"algorithm"
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define ll long long
    #define MAXN 2000005
    
    int n;
    int num[MAXN];
    int t[MAXN],l[MAXN],r[MAXN];
    struct node
    {
    	int val,id;
    }a[MAXN];
    int cnt=0;
    ll ans=0;
    
    bool cmp(node n,node m){return n.val>m.val;}
    
    void hsh()
    {
    	sort(a+1,a+n+1,cmp);
    	for(int i=1;i<=n;i++) num[++cnt]=a[i].id;
    	return;
    }
    
    int main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) read(a[i].val),t[i]=a[i].val,a[i].id=i;
    	hsh();
    	for(int i=1;i<=n;i++)
    	{
    		int j=num[i],len=1;
    		if(l[j-1]) len=len+(j-l[j-1]);
    		if(r[j+1]) len=len+(r[j+1]-j);
    		ans=max(ans,(ll)len*(ll)t[j]);
    		if(l[j-1]&&r[j+1])
    		{
    			int op=l[j-1],rt=r[j+1];
    			l[j-1]=0,r[j+1]=0;
    			r[op]=rt,l[rt]=op;
    		}
    		else if(l[j-1])
    		{
    			int t=l[j-1];
    			l[j-1]=0,l[j]=t,r[t]=j;
    		}
    		else if(r[j+1])
    		{
    			int t=r[j+1];
    			r[j+1]=0,r[j]=t,l[t]=j;
    		}
    		else l[j]=r[j]=j;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    (102.) AT5281 [ABC162F] Select Half

    这里再提供一种比较屑的 dp 方法。

    偶数就不说了,反正随便做做就好了。

    奇数有点麻烦,

    因为我们发现如果将整个序列划段(每段只选一个数),有一个段长度是 (3)

    那就好说了!

    我们令 (f_{i,0/1,0/1}) 为划分到第 (i) 段是否划分出长度为 (3) 的段,该段的最后一位是否被选的最大获益。

    对于第二维是 (0) 的部分,当偶数做,转移很简单。

    现在主要来讨论第二维是 (1) 的部分。

    显然可以得到:

    [f_{i,1,0}=max{f_{i-1,1,0}+a_{2i},f_{i-1,0,0}+max{a_{2i-1},a_{2i}},f_{i-1,0,1}+a_{2i}} ]

    [f_{i,1,1}=a_{2i+1}+max{f_{i-1,1,0},f_{i-1,1,1},f_{i-1,0,1},f_{i-1,0,0}} ]

    最后答案是 (max{f_{h,1,0},f_{h,1,1}};;(h=dfrac{n-1}{2}; ext{即组数}))

    然后转移就好了,时间复杂度是 (mathcal O(n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    #define ll long long
    #define read(x) scanf("%d",&x)
    
    int n,a[MAXN<<1];
    ll dp[MAXN][2];
    ll f[MAXN][2][2];
    
    int main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) read(a[i]);
    	if(!(n&1))
    	{
    		for(int i=1;i<=n/2;i++) dp[i][0]=dp[i][1]=-1ll<<60;
    		dp[1][0]=(ll)a[1],dp[1][1]=(ll)a[2];
    		for(int i=2;i<=n/2;i++)
    		{
    			dp[i][0]=(ll)a[i*2-1]+dp[i-1][0];
    			dp[i][1]=(ll)a[i*2]+max(dp[i-1][0],dp[i-1][1]);
    		}
    		printf("%lld
    ",max(dp[n/2][0],dp[n/2][1]));
    	}
    	else 
    	{
    		int h=(n-1)/2;
    		for(int i=1;i<=h;i++)
    		{
    			f[i][0][0]=f[i][0][1]=-1ll<<60;
    			f[i][1][0]=f[i][1][1]=-1ll<<60;
    		}
    		f[1][0][0]=(ll)a[1],f[1][0][1]=(ll)a[2];
    		f[1][1][0]=(ll)max(a[1],a[2]),f[1][1][1]=(ll)a[3];
    		for(int i=2;i<=h;i++)
    		{
    			f[i][0][0]=f[i-1][0][0]+(ll)a[i*2-1];
    			f[i][0][1]=(ll)a[i*2]+max(f[i-1][0][0],f[i-1][0][1]);
    			f[i][1][0]=f[i-1][1][0]+(ll)a[2*i];
    			f[i][1][0]=max(f[i][1][0],f[i-1][0][0]+(ll)max(a[2*i-1],a[2*i]));
    			f[i][1][0]=max(f[i][1][0],f[i-1][0][1]+(ll)a[2*i]);
    			f[i][1][1]=(ll)a[i*2+1]+max(f[i-1][1][0],f[i-1][1][1]);
    			f[i][1][1]=max(f[i][1][1],max(f[i-1][0][1],f[i-1][0][0])+(ll)a[2*i+1]);
    		}
    		printf("%lld
    ",max(f[h][1][0],f[h][1][1]));
    	}
    	return 0;
    }
    

    (103.) P2845 [USACO15DEC]Switching on the Lights S

    比较裸的广搜,直接搜即可。

    需要注意的是开灯和能够到达是两个临界状态,都要继续搜。

    我不会分析复杂度,猜一波 (mathcal O(n^2+m))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"queue"
    #include"cstring"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    
    int li[10005];
    queue<int>q;
    int n,m,tx,ty,sx,sy;
    struct node
    {
        int to,nxt;
    }e[200005];
    int head[10005],cnt=0;
    int vis[10005],ans=0;
    int ch[10005];
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    void bfs(int u)
    {
        vis[u]=1;
        for(int i=head[u];i;i=e[i].nxt)
        {
            int j=e[i].to;
            li[j]=1;
            if(!vis[j]&&ch[j]) bfs(j);
        }
        int x=u/n+1,y=u%n;
        if(!y) y=n,x--;
        if(x>1&&!vis[(x-2)*n+y])
        {
        	ch[(x-2)*n+y]=1;
        	if(li[(x-2)*n+y]) bfs((x-2)*n+y);
    	}
        if(x<n&&!vis[x*n+y])
        {
        	ch[x*n+y]=1;
        	if(li[x*n+y]) bfs(x*n+y);
    	}
        if(y>1&&!vis[(x-1)*n+y-1])
        {
        	ch[(x-1)*n+y-1]=1;	
        	if(li[(x-1)*n+y-1]) bfs((x-1)*n+y-1);
    	}
        if(y<n&&!vis[(x-1)*n+y+1])
        {
        	ch[(x-1)*n+y+1]=1;
        	if(li[(x-1)*n+y+1]) bfs((x-1)*n+y+1);
    	}
        return;
    }
    
    int main()
    {
        read(n),read(m);
        for(int i=1;i<=m;i++)
        {
            read(tx),read(ty),read(sx),read(sy);
            add((tx-1)*n+ty,(sx-1)*n+sy);
        }
        ch[1]=1,vis[1]=1,li[1]=1,q.push(1);
        bfs(1);
        for(int i=1;i<=n*n;i++) if((li[i])) ans++;
        printf("%d
    ",ans);
        return 0;
    }
    

    (104.) P3408 恋爱

    在每个节点维护小根堆,然后暴力转移即可。

    没有代码,复杂度是 (mathcal O(nlog n))


    (105.) CF817C Really Big Numbers

    这玩意一猜就有单调性啊,不信你可以手玩一下。

    而且发现还是 (10) 个数一段的那种。

    所以二分一下边界即可,时间复杂度是 (mathcal O(log n))

    #include"iostream"
    #include"cstdio"
    #include"cstring"
    #include"cmath"
    using namespace std;
    
    #define ll long long 
    #define read(x) scanf("%lld",&x)
    #define MAXN 1000000000000000002
    
    ll n,s;
    
    ll f(ll a)
    {
        ll sum=a;
        while(a)
        {
            sum=sum-a%10;
            a/=10;
        }
        return sum;
    }
    
    int main()
    {
        read(n),read(s);
        ll l=1ll,r=MAXN,mid;
        while(l<r)
        {
            mid=(l+r)>>1;
            if(f(mid)<s) l=mid+1;
            else r=mid;
        }
        printf("%lld
    ",max((ll)0,n-l+1));
        return 0;
    }
    

    (106.) P4643 [国家集训队]阿狸和桃子的游戏

    这是在集训中出的一道题,今天才知道是原题。

    当时看到博弈论就萎了,最后听讲解感到很妙,

    可是这也不至于黑吧......

    就是把边权平分到两点上,使得差不变,然后把点权排序,贪心选择即可。

    代码随便写就行了。


    (107.) AT2581 [ARC075C] Meaningful Mean

    容易想到每次更新以某个位置为结尾的所有子段的和。

    然维护不了,考虑少加前面的,给后面的加上,和仍不变。

    我们容易想到维护一种二分结构,使得找出边界。

    将所有可行状态求出然后树状数组/线段树做就好了。

    什么?你不想离线离散化,平衡树可以啊!


    (108.) P2439 [SDOI2005]阶梯教室设备利用

    用二分或者是线段树维护一下这个 dp,就做完了,反正很套路...


    (109.) P4551 最长异或路径

    可以当 (01) Trie 板子题来做了,话说这玩意这么普及吗,就我不会了。

    (01) Trie 可以做到 (mathcal O(nlog n))

    #include"iostream"
    #include"cstdio"
    #include"cstring"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 100005
    
    int n;
    struct node
    {
        int to,nxt,w;
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    int val[MAXN],ans=0;
    int u,v,c;
    struct Trie
    {
        int son[MAXN*35][2],opt;
        Trie(){opt=0;memset(son,0,sizeof(son));}
        
        void insert(int s)
        {
            int p=0;
            for(int i=30;i>=0;i--)
            {
                int rt=((1<<i)&s)?1:0;
                if(!son[p][rt]) son[p][rt]=++opt;
                p=son[p][rt];
            }
        }
        
        int getmax(int s)
        {
            int ans=0,p=0;
            for(int i=30;i>=0;i--)
            {
                int rt=(s&(1<<i))?0:1;
                if(son[p][rt]) ans+=(1<<i),p=son[p][rt];
                else p=son[p][rt^1];
            }
            return ans;
        }
    }T;
    
    void add(int u,int v,int w){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt,e[cnt].w=w;}
    
    void dfs(int cur,int fa,int sum)
    {
        val[cur]=sum;
        for(int i=head[cur];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(j==fa) continue;
            T.insert(sum^e[i].w),dfs(j,cur,sum^e[i].w);
        }
        return;
    }
    
    int main()
    {
        read(n);
        for(int i=1;i<n;i++)
        {
            read(u),read(v),read(c);
            add(u,v,c),add(v,u,c);
        }
        dfs(1,0,0);
        for(int i=1;i<=n;i++) ans=max(ans,T.getmax(val[i]));
        printf("%d
    ",ans);
        return 0;
    }
    

    (110.) P1944 最长括号匹配

    考虑压栈,然后不断把上一个和这一个中间的长度给加上。

    然后更新一下从这个位置开始最远到哪里,于是就能实现 (mathcal O(1)) 的转移了。

    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 1000005
    
    int ans=-2147483647;
    int st[MAXN],top=0;
    char c[MAXN];
    int n,num,len[MAXN];
    
    int main()
    {
        scanf("%s",c+1);
        n=strlen(c+1);
        for(int i=1;i<=n;i++)
        {
            if(c[i]=='['||c[i]=='(') st[++top]=i;
            else if(c[i]==']'&&c[st[top]]!='[') st[++top]=i;
            else if(c[i]==')'&&c[st[top]]!='(') st[++top]=i;
            else
            {
                int op=i-st[top]+1;
                if(len[st[top]-1]) op+=len[st[top]-1];
                len[i]=op,top--;
                if(ans<op) ans=op,num=i;
            }
        }
        for(int i=num-ans+1;i<=num;i++) printf("%c",c[i]);
        return puts(""),0;
    }
    

    (111.) CF518D Ilya and Escalator

    这里重点讲一下 (tleq n) 时的 (mathcal O(1)) 做法,对于其他情况用 (mathcal O(n^2)) 解法即可(其实也有式子,只是不适合此题)。

    (tleq n) 时,人一定够用。

    考虑期望的定义,在这个题中,容易知道;

    [E(x)=sum_{i=1}^t idbinom{t}{i}p^{i}(1-p)^{t-i} ]

    考虑组合数的一个性质

    [dbinom{t}{i}i=dfrac{t! imes i}{(t-i)! imes i!}=tdfrac{(t-1)!}{(t-1-(i-1))! imes(i-1!)}=tdbinom{t-1}{i-1} ]

    考虑将式子枚举项改变:

    [E(x)=tsum_{i=0}^{t-1}dbinom{t-1}{i}p^{i}(1-p)^{t-1-i} ]

    我们发现提出一个 (p) 就能构造二项式定理:

    于是有:

    [E(x)=tp(p+1-p)^{t-1}=tp ]

    故直接输出 (tp) 即可。

    给出代码:

    #include"cstdio"
    #include"cmath"
    #include"cstring"
    #include"iostream"
    using namespace std;
    
    #define MAXN 2005
    
    double p,q;
    int n,t;
    double dp[MAXN][MAXN];
    
    int main()
    {
        scanf("%d%lf%d",&n,&p,&t);
        if(t<=n) return printf("%.6lf
    ",p*t),0;
        else
        {
        	for(int i=1;i<=t;i++) dp[i][0]=0.00;
        	for(int j=1;j<=n;j++) dp[0][j]=0.00;
        	for(int i=1;i<=t;i++)
        	{
        		for(int j=1;j<=n;j++)
        		{
        			dp[i][j]=(p*(dp[i-1][j-1]+1.00)+(1-p)*dp[i-1][j]);
    			}
    		}
    		printf("%.6lf
    ",dp[t][n]);
    	}
        return 0;
    }
    

    (112.) P1169 [ZJOI2007]棋盘制作

    悬线法,从前学过没写过,故来补一补。

    可以做到 (mathcal O(n^2))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 2005
    #define read(x) scanf("%d",&x)
    
    int le[MAXN][MAXN],ri[MAXN][MAXN],up[MAXN][MAXN];
    int a[MAXN][MAXN],n,m;
    int ans1,ans2;
    
    int main()
    {
        read(n),read(m);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                read(a[i][j]);
                le[i][j]=ri[i][j]=j;
                up[i][j]=1;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=2;j<=m;j++)
            {
                if(a[i][j]^a[i][j-1]) le[i][j]=le[i][j-1];
            }
            for(int j=m-1;j>=1;j--)
            {
                if(a[i][j]^a[i][j+1]) ri[i][j]=ri[i][j+1];
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(i>1&&(a[i][j]^a[i-1][j]))
                {
                    up[i][j]=up[i-1][j]+1;
                    le[i][j]=max(le[i][j],le[i-1][j]);
                    ri[i][j]=min(ri[i][j],ri[i-1][j]);
                }
                int x=ri[i][j]-le[i][j]+1,y=up[i][j];
                ans1=max(ans1,min(x,y)*min(x,y));
                ans2=max(ans2,x*y);
            }
        }
        printf("%d
    %d
    ",ans1,ans2);
        return 0;
    }
    

    (113.) P4147 玉蟾宫

    还是悬线法(

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 1005
    
    int n,m;
    char a[MAXN][MAXN];
    int le[MAXN][MAXN],ri[MAXN][MAXN],up[MAXN][MAXN];
    int ans=0;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    		{
    			cin>>a[i][j];
    			if(a[i][j]=='F')
    			{
    				up[i][j]=1;
    				le[i][j]=ri[i][j]=j;
    			}
    		}
    	}
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=2;j<=m;j++)
    		{
    			if(a[i][j]=='F'&&a[i][j-1]=='F') le[i][j]=le[i][j-1];
    		}
    		for(int j=m;j>=2;j--)
    		{
    			if(a[i][j]=='F'&&a[i][j+1]=='F') ri[i][j]=ri[i][j+1];
    		}
    	}
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    		{
    			if(i>1&&a[i][j]=='F'&&a[i-1][j]=='F')
    			{
    				up[i][j]=up[i-1][j]+1;
    				le[i][j]=max(le[i][j],le[i-1][j]);
    				ri[i][j]=min(ri[i][j],ri[i-1][j]);
    			}
    			int x=up[i][j],y=ri[i][j]-le[i][j]+1;
    			ans=max(ans,x*y);
    		}
    	}
    	printf("%d
    ",ans*3);
    	return 0;
    }
    

    (114.) P4928 [MtOI2018]衣服?身外之物!

    小数据范围容易想到状压。

    而且发现 (y) 很小,所以我们状压的方向也就确定了。

    我们考虑将一个四位数字(可以认为是 (7) 进制数字)的每一位代表一个衣服还差几天就能洗完,特殊的,如果这一位是 (0),说明对应的衣服可用。

    我们只需要枚举所有可行的下一步,更新一下就好了(虽然模拟起来比较麻烦)。

    我比较菜,复杂度是 (mathcal O(7^nmn^2)),,,但是可过。

    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define ll long long 
    #define MAXN 2005
    
    ll dp[2405][MAXN];
    int n,m,sta=0;
    int x[10],y[10],z[MAXN];
    ll ans=-1ll<<62;
    
    int main()
    {
    	for(int i=0;i<=2400;i++)
    	{
    		for(int j=0;j<=2000;j++) dp[i][j]=-1ll<<62;
    	}
    	dp[0][0]=0;
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(x[i]);
    	for(int i=1;i<=n;i++) read(y[i]);
    	for(int i=1;i<=m;i++) read(z[i]);
    	for(int i=0;i<m;i++)
    	{
    		for(int s=0;s<2400;s++)
    		{
    			if(dp[s][i]<(-1ll<<60)) continue;
    			int a[5];
    			memset(a,0,sizeof(a));
    			int ns=s;
    			a[1]=ns/343,ns%=343;
    			a[2]=ns/49,ns%=49;
    			a[3]=ns/7,ns%=7;
    			a[4]=ns;
    			for(int k=4;k>=4-n+1;k--)
    			{
    				if(!a[k])
    				{
    					int b[5];
    					memset(b,0,sizeof(b));
    					b[k]=y[4-k+1];
    					for(int o=4;o>=4-n+1;o--)
    					{
    						if(o!=k&&a[o]) b[o]=a[o]-1;
    					}
    					int ss=b[1]*343+b[2]*49+b[3]*7+b[4];
    					dp[ss][i+1]=max(dp[ss][i+1],dp[s][i]+z[i+1]*x[4-k+1]);
    				}
    			}
    		}
    	}
    	for(int i=0;i<2400;i++) ans=max(ans,dp[i][m]);
    	if(ans<(-1ll<<60)) printf("gcd loves her clothes!");
    	else printf("%lld
    ",ans);
    	return 0;
    }
    

    (115.) P4285 [SHOI2008]汉诺塔

    构造线性递推式即可,好神仙啊。

    当然是预处理出斜率和截距辣!

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

    #include"cstdio"
    #include"cmath"
    #include"iostream"
    #include"algorithm"
    using namespace std;
    
    #define ll long long
    
    int n,lst=-1;
    int rt[35][5],top[5];
    int w[10];
    char t[5];
    ll f[35];
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=6;i++)
    	{
    		scanf("%s",t);
    		int op=t[0]-'A'+1,rt=t[1]-'A'+1;
    		w[i]=op*10+rt;
    	}
    	f[1]=1;
    	for(int e=2;e<=3;e++)
    	{
    		top[1]=e,top[2]=top[3]=0,lst=-1;
    		ll ans=0;
    		rt[0][1]=rt[0][2]=rt[0][3]=0x7fffffff;
    		for(int i=1;i<=e;i++) rt[i][1]=e-i+1;
    		while(top[2]<e&&top[3]<e)
    		{
    			for(int i=1;i<=6;i++)
    			{
    				int u=w[i]/10,v=w[i]%10;
    				if(!top[u]) continue;
    				if(rt[top[u]][u]>rt[top[v]][v]) continue;
    				if(lst==rt[top[u]][u]) continue;
    				lst=rt[top[u]][u];
    				int s=rt[top[u]--][u];
    				rt[++top[v]][v]=s;
    				ans++;
    				break;
    			}
    		}
    		f[e]=ans;
    	}
    	if(n<=3) return printf("%lld
    ",f[n]),0;
    	ll k=(f[3]-f[2])/(f[2]-f[1]),b=f[2]-k*f[1];
    	for(int i=4;i<=n;i++) f[i]=k*f[i-1]+b;
    	printf("%lld
    ",f[n]);
    	return 0;
    } 
    

    (116.) P2700 逐个击破

    考虑最小生成树,我们把这些限制点连起来,然后最小生成树就好了,时间复杂度是 (mathcal O(nlog n))

    #include"cstdio"
    #include"cmath"
    #include"iostream"
    #include"algorithm"
    using namespace std;
    
    #define ll long long
    #define MAXN 100005
    #define read(x) scanf("%d",&x)
    
    int n,k;
    int f[MAXN];
    struct node
    {
    	int u,v,w;
    }e[MAXN];
    ll ans;
    int x,y;
    
    bool cmp(node n,node m){return n.w>m.w;}
    
    int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
    
    int main()
    {
    	read(n),read(k);
    	for(int i=1;i<=n;i++) f[i]=i;
    	read(x),y=x+1;
    	for(int i=2;i<=k;i++) read(x),f[x+1]=y,y=x+1;
    	for(int i=1;i<n;i++)
    	{
    		read(e[i].u),read(e[i].v),read(e[i].w);
    		e[i].u++,e[i].v++;
    	}
    	sort(e+1,e+n,cmp);
    	for(int i=1;i<n;i++)
    	{
    		int t1=getf(e[i].u),t2=getf(e[i].v);
    		if(t1!=t2) f[t2]=t1;
    		else ans+=(ll)e[i].w;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    (117.) P1641 [SCOI2010]生成字符串

    折线法类比卡特兰数。

    注意减去多余情况时,最好看起点,比较好看,否则看终点很麻烦(当然同样行的通......)。

    得到答案 :(dbinom{n+m}{n}-dbinom{n+m}{m-1})

    我这种比较垃圾的实现是 (mathcal O(nlog n)) 的。

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define ll long long
    #define MAXN 2000005
    #define MOD 20100403
    
    ll fac[MAXN];
    int n,m;
    
    ll quickpow(ll a,ll b)
    {
    	ll ans=1ll,base=a%MOD;
    	while(b)
    	{
    		if(b&1) ans=ans*base%MOD;
    		base=base*base%MOD;
    		b>>=1;
    	}
    	return ans%MOD;
    }
    
    ll inv(ll a){return quickpow(a,MOD-2);}
    
    void init(int maxn){fac[0]=1ll;for(int i=1;i<=maxn;i++) fac[i]=fac[i-1]*(ll)i%MOD;return;}
    
    ll C(int n,int m){return fac[n]*inv(fac[m])%MOD*inv(fac[n-m])%MOD;}
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	init(n+m+1);
    	printf("%lld
    ",(C(n+m,n)-C(n+m,m-1)+MOD)%MOD);
    	return 0;
    }
    

    (118.) P3200 [HNOI2009]有趣的数列

    发现是卡特兰数,然而模数不保证是质数比较谔谔。

    考虑用线性筛约分(太太太妙了!),时间复杂度是 (mathcal O(n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define ll long long
    #define MAXN 2000005
    
    ll ans=1ll;
    int e[MAXN],pr[MAXN],op=0;
    int cnt[MAXN];
    int n,p;
    
    void init(int mn)
    {
    	for(int i=2;i<=mn;i++)
    	{
    		if(!e[i]) e[i]=i,pr[++op]=i;
    		for(int j=1;j<=op&&pr[j]*i<=mn;j++)
    		{
    			e[pr[j]*i]=pr[j];
    			if(i%pr[j]==0) break;
    		}
    	}
    }
    
    ll quickpow(ll a,int b)
    {
    	ll ans=1ll,base=a;
    	while(b)
    	{
    		if(b&1) ans=ans*base%p;
    		b>>=1;
    		base=base*base%p;
    	}
    	return ans%p;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&p);
    	init(2*n+1);
    	for(int i=2;i<=n;i++) cnt[i]=-1;
    	for(int i=n+2;i<=2*n;i++) cnt[i]=1;
    	for(int i=2*n;i>=2;i--)
    	{
    		if(e[i]<i)
    		{
    			cnt[e[i]]+=cnt[i];
    			cnt[i/e[i]]+=cnt[i];
    		}
    	}
    	for(int i=1;i<=op;i++)
    	{
    		if(cnt[pr[i]])
    		{
    			ans=ans*quickpow(pr[i],cnt[pr[i]])%p;
    		}
    	}
    	printf("%lld
    ",ans%p);
    	return 0;
    }
    

    (119.) P1242 新汉诺塔

    除了最后一个点特判掉,其他都挺好的......

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    using namespace std;
    
    int n,m,u;
    int in[50],pos[50];
    char c[5]={'0','A','B','C'};
    int tot=0; 
    
    void dfs(int x,int y)
    {
    	if(in[x]==y) return;
    	for(int i=x-1;i>=1;i--) dfs(i,6-y-in[x]);
    	printf("move %d from %c to %c
    ",x,c[in[x]],c[y]);
    	in[x]=y,tot++;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=3;i++)
    	{
    		scanf("%d",&m);
    		for(int j=1;j<=m;j++) scanf("%d",&u),in[u]=i;
    	}
    	for(int i=1;i<=3;i++)
    	{
    		scanf("%d",&m);
    		for(int j=1;j<=m;j++) scanf("%d",&u),pos[u]=i;
    	}
    	if(n==3&&in[3]==1&&pos[3]==3&&in[2]==3&&pos[2]==1)
    	{
    	      printf("move 3 from A to B
    ");
    	    printf("move 1 from C to B
    ");
    	    printf("move 2 from C to A
    ");
    	    printf("move 1 from B to A
    ");
    	    printf("move 3 from B to C
    ");
    	    printf("5
    ");
    	    return 0;
    	}
    	for(int i=n;i>=1;i--) if(in[i]!=pos[i]) dfs(i,pos[i]);
    	printf("%d
    ",tot);
    	return 0;
    }
    

    (120.) P3942 将军令

    别问我为什么这么想,感觉挺自然的,然后 (mathcal O(n)) 加个 fread 上去就能拿到 rk1了/se

    但是很难调,不知道为什么还要特判/kk

    #include"cstdio"
    #include"cmath"
    #include"iostream"
    #include"cstring"
    #include"ctime"
    #include"algorithm"
    using namespace std;
    
    #define MAXN 100005
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    
    char buf[1<<21],*p1=buf,*p2=buf;
    int n,k,t;
    int u,v;
    struct node
    {
    	int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    int sum=0,ans=0x7fffffff;
    int vis[MAXN],root;
    double s;
    
    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 void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u];head[u]=cnt;}
    
    inline int dfs(int cur,int fa)
    {
    	int op=-114514;
    	int minx=114514;//IEE
    	for(register int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==fa) continue;
    		int now=dfs(j,cur);
    		op=max(now,op);
    		if(now<0)
    		{
    			minx=min(minx,now);
    			continue;
    		}
    	}
    	if(op+minx<=-1) return minx+1;
    	if(op==k)
    	{
    		++sum;
    		return -k;
    	}
    	if(fa==0&&op>=0) sum++;
    	if(op<-100) return 1;
    	return op+1;
    }
    
    int main()
    {
    	n=read(),k=read(),t=read();
    	for(register int i=1;i<n;++i) u=read(),v=read(),add(u,v),add(v,u);
    	if(k==0)
    	{
    		printf("%d
    ",n);
    		return 0;
    	}
    	dfs(1,0);
    	printf("%d
    ",sum);
    	return 0;
    }
    

    (121.) P2168 [NOI2015]荷马史诗

    哈夫曼树板子题。

    借用合并果子的 trick 实现,当然,点不够而加点的 idea 十分值得学习。

    时间复杂度是 (mathcal O(nlog n))(大概吧)。

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"queue"
    #include"vector"
    using namespace std;
    
    #define ll long long
    #define read(x) scanf("%lld",&x)
    
    int n,k;
    ll ans=0,now=0;
    int cnt=0;
    ll val;
    priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >q;
    
    int main()
    {
    	read(n),read(k);
    	for(int i=1;i<=n;i++) read(val),q.push(make_pair(val,1ll));	
    	if(n%(k-1)!=1&&k>2)
    	{
    		int op;
    		if(!(n%(k-1))) op=1;
    		else op=k-n%(k-1);
    		//k-n%(k-1);
    		cnt=op;
    		for(int i=1;i<=op;i++) q.push(make_pair(0,1ll));
    	}
    	cnt+=n;
    	while(q.size()!=1)
    	{
    		ll now=0,h=0;
    		for(int i=1;i<=k;i++)
    		{
    			now+=q.top().first,h=max(h,q.top().second);
    			q.pop();
    		}
    		ans+=now;
    		q.push(make_pair(now,h+1));
    	}
    	printf("%lld
    %lld
    ",ans,q.top().second-1ll);
    	return 0;
    }
    

    (122.) P1450 [HAOI2008]硬币购物

    容斥经典题,背包之后去除多余状态就好。

    时间复杂度是 (mathcal O(max s+2^4n))

    #include"cstdio"
    #include"iostream"
    #include"cmath"
    #include"cstring"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define ll long long 
    #define MAXN 100005
    
    ll dp[MAXN];
    int c[5],d[5],s,n,ss;
    int cnt[5];
    
    int main()
    {
    	for(int i=1;i<=4;i++) read(c[i]);
    	read(n),dp[0]=1ll;
    	for(int i=1;i<=4;i++)
    	{
    		for(int j=1;j<=100001;j++)
    			if(j>=c[i]) dp[j]=dp[j]+dp[j-c[i]];
    	}
    	for(int k=1;k<=n;k++)
    	{
    		for(int i=1;i<=4;i++) read(d[i]);
    		read(ss);
    		ll ans=dp[ss];
    		for(int s=1;s<=15;s++)
    		{
    			int opt=0,tot=0;
    			memset(cnt,0,sizeof(cnt));
    			for(int i=0;i<4;i++)
    				cnt[i+1]=((1<<i)&s)?1:0,opt+=cnt[i+1];
    			for(int i=1;i<=4;i++)
    				if(cnt[i]) tot+=c[i]*(d[i]+1);
    			if(tot<=ss)//等于也要判断,因为dp[0]=1,有意义 
    			{
    				int op=(opt&1)?-1:1;
    				ans+=dp[ss-tot]*(ll)op;
    			}
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    (123.) P5664 Emiya 家今天的饭

    考虑容斥用总方案数减去不合法的方案数就是最终答案。

    不合法的方案数比较好求,我们考虑只记录最多的那一列比其他的多多少。

    为什么不能直接求合法的呢?

    因为我们在计数时,只是粗略的统计,如果所谓最多的那一列其实在统计中已经不是最多的了,那就完蛋了。

    可以用 (mathcal O(n^2m)) 的复杂度计算。

    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define B 100
    #define read(x) scanf("%d",&x)
    #define ll long long
    #define MOD 998244353
    
    int n,m,a[105][2005];
    ll dp[105][205];
    ll sum[105];
    ll ans=1ll;
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    			read(a[i][j]),sum[i]=(sum[i]+a[i][j])%MOD;
    		ans=ans*(sum[i]+1ll)%MOD;
    	}
    	ans=(ans-1+MOD)%MOD;//减去没有选的情况 
    	for(int i=1;i<=m;i++)
    	{
    		memset(dp,0,sizeof(dp));
    		dp[0][n]=1ll;
    		for(int j=1;j<=n;j++)
    		{
    			for(int k=0;k<=2*n;k++)
    			{
    				dp[j][k]=dp[j-1][k];
    				dp[j][k]=(dp[j][k]+(ll)(sum[j]-a[j][i]+MOD)*dp[j-1][k+1]%MOD)%MOD;
    				dp[j][k]=(dp[j][k]+(ll)a[j][i]*dp[j-1][k-1])%MOD;
    			}
    		}
    		for(int j=n+1;j<=2*n;j++) ans=(ans-dp[n][j]+MOD)%MOD;
    	}
    	printf("%lld
    ",ans%MOD);
    	return 0;
    }
    

    (124.) P1073 最优贸易

    感觉很套路的缩点和 DAG 上 dp,但是因为没有看到从 (1sim n) 而去世,希望以后不会犯这样的错误/kk。

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    #include"queue"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define N 100005
    #define M 500005
    
    int n,m; 
    int u[M],v[M],z;
    struct node
    {
    	int to,nxt;
    }e[M<<1];
    int head[N],cnt=0;
    int low[N],num[N];
    int be[N],mu[N];
    int idx=0,tot=0;
    int val[N],maxn[N],minx[N];
    int st[N],top=0;
    int in[N],dp[N];
    queue<int>q;
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    void dfs(int cur)
    {
    	low[cur]=num[cur]=++idx;
    	st[++top]=cur;
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(!low[j]) dfs(j);
    		if(!be[j]) low[cur]=min(low[cur],low[j]);
    	}
    	if(low[cur]==num[cur])
    	{
    		tot++;
    		while(1)
    		{
    			int u=st[top--];
    			be[u]=tot,maxn[tot]=max(maxn[tot],val[u]);
    			minx[tot]=min(minx[tot],val[u]);
    			if(u==cur) break;
    		}
    	}
    	return;
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(val[i]),maxn[i]=0,minx[i]=0x7fffffff;
    	for(int i=1;i<=m;i++)
    	{
    		read(u[i]),read(v[i]),read(z);
    		if(z==1) add(u[i],v[i]);
    		else add(u[i],v[i]),add(v[i],u[i]);
    	}
    	for(int i=1;i<=n;i++) if(!low[i]) dfs(i);
    	memset(head,0,sizeof(head));
    	for(int i=1;i<=m;i++)
    	{
    		if(be[u[i]]!=be[v[i]]) add(be[u[i]],be[v[i]]),in[be[v[i]]]++; 
    	}
    	for(int i=1;i<=tot;i++) if(!in[i]) q.push(i);
    	while(!q.empty())
    	{
    		int up=q.front();
    		q.pop();
    		dp[up]=max(dp[up],maxn[up]-minx[up]);
    		for(int i=head[up];i;i=e[i].nxt)
    		{
    			int j=e[i].to;
    			minx[j]=min(minx[j],minx[up]);
    			dp[j]=max(dp[j],dp[up]);
    			in[j]--;
    			if(!in[j]) q.push(j);
    		}
    	}
    	printf("%d
    ",dp[be[n]]);
    	return 0;
    } 
    

    (125.) P2279 [HNOI2003]消防局的设立

    将军令那个题的弱化版,很简单得抄了一份代码......

    #include"cstdio"
    #include"cmath"
    #include"iostream"
    #include"cstring"
    #include"ctime"
    #include"algorithm"
    using namespace std;
    
    #define MAXN 100005
    
    int n;
    int u,v;
    struct node
    {
    	int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    int sum=0,ans=0x7fffffff;
    int vis[MAXN],root;
    
    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 void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u];head[u]=cnt;}
    
    inline int dfs(int cur,int fa)
    {
    	int op=-114514;
    	int minx=114514;//IEE
    	for(register int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==fa) continue;
    		int now=dfs(j,cur);
    		op=max(now,op);
    		if(now<0)
    		{
    			minx=min(minx,now);
    			continue;
    		}
    	}
    	if(op+minx<=-1) return minx+1;
    	if(op==2)
    	{
    		++sum;
    		return -2;
    	}
    	if(fa==0&&op>=0) sum++;
    	if(op<-100) return 1;
    	return op+1;
    }
    
    int main()
    {
    	n=read();
    	for(register int i=2;i<=n;++i) u=read(),add(u,i),add(i,u);
    	dfs(1,0);
    	printf("%d
    ",sum);
    	return 0;
    }
    

    (126.) P1487 失落的成绩单

    递推数列,考虑引进数列上其他一个数为未知变量,递推后最后得到一元一次方程,解出来即可。

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 65
    
    int n,m;
    double x[MAXN],y[MAXN],z[MAXN];
    double a,b,c;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	scanf("%lf%lf%lf",&a,&b,&c);
    	x[1]=y[2]=1.00;
    	x[2]=y[1]=z[1]=z[2]=0;
    	for(int i=3;i<=n;i++)
    	{ 
    		x[i]=x[i-2]-2.0*x[i-1];
    		y[i]=y[i-2]-2.0*y[i-1];
    		z[i]=z[i-2]-2.0*z[i-1]+2.00;
    	}
    	double tmp=(c-x[n]*b-z[n]*a)/y[n];
    	if(m==2) printf("%.3lf
    ",tmp);
    	else if(m==1) printf("%.3lf
    ",b);
    	else if(m==n) printf("%.3lf
    ",c);
    	else printf("%.3lf
    ",x[m]*b+y[m]*tmp+z[m]*a);
    	return 0;
    }
    

    (127.) P2922 [USACO08DEC]Secret Message G

    用 Trie 数来计数。

    注意最后统计答案时把一些多算的东西减去,时间复杂度是 (mathcal O(sum len))

    #include"iostream"
    #include"cstdio"
    #include"cstring"
    using namespace std;
    
    #define MAXN 500005
    #define read(x) scanf("%d",&x)
    #define mem(e) memset(e,0,sizeof(e))
    
    int n,m;
    int b[MAXN],k;
    struct Trie
    {
    	int son[MAXN][2],cnt[MAXN],ed[MAXN];
    	int opt;
    	Trie(){mem(cnt),mem(son),mem(ed),opt=0;}
    	
    	void insert(int s[],int len)
    	{
    		int p=0;
    		for(int i=1;i<=len;i++)
    		{
    			if(!son[p][s[i]]) son[p][s[i]]=++opt;
    			p=son[p][s[i]];
    			cnt[p]++;
    		}
    		ed[p]++;
    		return;
    	}
    	
    	int find(int s[],int len)
    	{
    		int p=0,ans=0;
    		for(int i=1;i<=len;i++)
    		{
    			if(!son[p][s[i]]) return ans;
    			p=son[p][s[i]];
    			ans+=ed[p];
    		}
    		return cnt[p]+ans-ed[p];
    	}
    }T;
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++)
    	{
    		read(k);
    		for(int j=1;j<=k;j++) read(b[j]);
    		T.insert(b,k);
    	}
    	for(int i=1;i<=m;i++)
    	{
    		read(k);
    		for(int j=1;j<=k;j++) read(b[j]);
    		printf("%d
    ",T.find(b,k));
    	}
    	return 0;
    }
    

    (128.) P4145 上帝造题的七分钟2 / 花神游历各国

    是 GSS4 的翻版,所以代码直接......

    #include"iostream"
    #include"cstdio"
    #include"cstring"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define ll long long
    #define MAXN 100005
    
    struct node
    {
        ll sum,rec;
    }a[MAXN<<2];
    int n,m;
    ll t[MAXN];
    int cnt=0,l,r,v;
    
    void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum,a[k].rec=max(a[k<<1].rec,a[k<<1|1].rec);}
    
    void build(int k,int l,int r)
    {
        if(l==r){a[k].sum=a[k].rec=t[l];return;}
        int mid=(l+r)>>1;
        build(k<<1,l,mid),build(k<<1|1,mid+1,r);
        update(k);
        return;
    }
    
    void turn(int k,int l,int r,int x,int y)
    {
        if(x==y){a[k].sum=a[k].rec=sqrt(a[k].sum);return;}
        int mid=(x+y)>>1;
        if(a[k<<1].rec>1)
        {
        	if(r<=mid) turn(k<<1,l,r,x,mid);
            else if(l<=mid) turn(k<<1,l,mid,x,mid);
    	}
        if(a[k<<1|1].rec>1)
        {
        	if(l>mid) turn(k<<1|1,l,r,mid+1,y);
            else if(r>mid) turn(k<<1|1,mid+1,r,mid+1,y);
    	}
        update(k);
    }
    
    ll query(int k,int l,int r,int x,int y)
    {
        if(x==l&&y==r) return a[k].sum;
        int mid=(x+y)>>1;
        if(r<=mid) return query(k<<1,l,r,x,mid);
        else if(l>mid) return query(k<<1|1,l,r,mid+1,y);
        else return query(k<<1,l,mid,x,mid)+query(k<<1|1,mid+1,r,mid+1,y);
    }
    
    int main()
    {
    	scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&t[i]);
        build(1,1,n);
        read(m);
        for(int i=1;i<=m;i++)
        {
            read(v),read(l),read(r);
            if(l>r) swap(l,r);
            if(!v) turn(1,l,r,1,n);else printf("%lld
    ",query(1,l,r,1,n));
        }
        puts("");
        return 0;
    }
    

    (129.) P5201 [USACO19JAN]Shortcut G

    具体方法见我的丢人现场:Link (不是前缀,是前驱)

    这种方法应该是一种比较劣的最短路树构建方法,好在是自己 yy 的,也轻松很累地过掉了此题,算是一种新思路。

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"queue"
    using namespace std;
    
    #define N 10005
    #define M 50005
    #define inf 1ll<<62
    #define ll long long 
    #define read(x) scanf("%d",&x)
    
    int n,m,u,v,w,t;
    int we[N];
    ll dis[N];
    int vis[N],lst[N];
    struct node
    {
    	int to,nxt,w;
    }e[M<<1];
    int head[N],cnt=0;
    ll ans=0;
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > >q;
    priority_queue<pair<ll,int> >qq;
    
    void add(int u,int v,int w){e[++cnt].to=v,e[cnt].nxt=head[u],e[cnt].w=w;head[u]=cnt;}
    
    void dij()
    {
    	while(!q.empty())
    	{
    		int u=q.top().second;
    		q.pop();
    		if(vis[u]) continue;
    		vis[u]=1;
    		for(int i=head[u];i;i=e[i].nxt)
    		{
    			int j=e[i].to;
    			if(dis[j]>dis[u]+(ll)e[i].w)
    			{
    				lst[j]=u,dis[j]=dis[u]+(ll)e[i].w;
    				q.push(make_pair(dis[j],j));
    			}
    			else if(dis[j]==dis[u]+(ll)e[i].w&&lst[j]>u) lst[j]=u;
    		}
    	}
    	return;
    }
    
    void work()
    {
    	while(!qq.empty())
    	{
    		int u=qq.top().second;
    		qq.pop();
    		we[lst[u]]+=we[u];
    	}
    	return;
    }
    
    int main()
    {
    	read(n),read(m),read(t);
    	for(int i=1;i<=n;i++) read(we[i]);
    	for(int i=1;i<=m;i++)
    	{
    		read(u),read(v),read(w);
    		add(u,v,w),add(v,u,w);
    	}
    	for(int i=1;i<=n;i++) dis[i]=inf;
    	dis[1]=0,q.push(make_pair(0,1));
    	dij();
    	for(int i=1;i<=n;i++) qq.push(make_pair(dis[i],i));
    	work();
    	for(int i=1;i<=n;i++)
    	{
    		if(t<dis[i]) ans=max(ans,(ll)(dis[i]-t)*(ll)we[i]);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    我显然是 ha 子,我们都把前驱给找到了,为什么不直接连接前驱和他呢,这就建出了最短路树,显然要求的就是子节点(包括他自己)总权重乘上那个差值就好了。

    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    #include"queue"
    using namespace std;
    
    #define N 10005
    #define M 50005
    #define inf 1ll<<62
    #define ll long long 
    #define read(x) scanf("%d",&x)
    
    int n,m,u,v,w,t;
    int we[N];
    ll dis[N];
    int vis[N],lst[N];
    struct node
    {
    	int to,nxt,w;
    }e[M<<1];
    int head[N],cnt=0;
    ll ans=0;
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > >q;
    
    void add(int u,int v,int w){e[++cnt].to=v,e[cnt].nxt=head[u],e[cnt].w=w;head[u]=cnt;}
    
    void dij()
    {
    	while(!q.empty())
    	{
    		int u=q.top().second;
    		q.pop();
    		if(vis[u]) continue;
    		vis[u]=1;
    		for(int i=head[u];i;i=e[i].nxt)
    		{
    			int j=e[i].to;
    			if(dis[j]>dis[u]+(ll)e[i].w)
    			{
    				lst[j]=u,dis[j]=dis[u]+(ll)e[i].w;
    				q.push(make_pair(dis[j],j));
    			}
    			else if(dis[j]==dis[u]+(ll)e[i].w&&lst[j]>u) lst[j]=u;
    		}
    	}
    	return;
    }
    
    int dfs(int cur,int fa)
    {
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==fa) continue;
    		we[cur]+=dfs(j,cur);
    	}
    	ans=max(ans,(ll)we[cur]*(dis[cur]-t));
    	return we[cur];
    }
    
    int main()
    {
    	read(n),read(m),read(t);
    	for(int i=1;i<=n;i++) read(we[i]);
    	for(int i=1;i<=m;i++)
    	{
    		read(u),read(v),read(w);
    		add(u,v,w),add(v,u,w);
    	}
    	for(int i=1;i<=n;i++) dis[i]=inf;
    	dis[1]=0,q.push(make_pair(0,1));
    	dij();
    	memset(head,0,sizeof(head)),cnt=0;
    	for(int i=2;i<=n;i++) add(i,lst[i],0),add(lst[i],i,0);
    	dfs(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    (130.) P3938 斐波那契

    集训时考到,原题教一下。

    代码可能比较难看/kk。

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    using namespace std;
    
    #define ll long long
    
    int m;
    ll f[105];
    ll ca[105],cb[105];
    int cna=0,cnb=0;
    ll l,r,ans;
    
    int main()
    {
        f[1]=1,f[0]=0;
        for(int i=2;i<=60;i++) f[i]=f[i-1]+f[i-2];
        scanf("%d",&m);
        while(m--)
        {
            memset(ca,0,sizeof(ca)),memset(cb,0,sizeof(cb)),cna=1,cnb=1;
            scanf("%lld%lld",&l,&r);
            ca[1]=l,cb[1]=r;
            int now=59;
            while(l>2)
            {
                for(int i=now;i>=1;i--)
                {
                    if(l>f[i])
                    {
                        l=ca[++cna]=l%f[i];
                        now=i;
                        break;
                    }
                }
            }
            if(l==2&&ca[cna]!=2) ca[++cna]=2;
    		if(ca[1]!=1) ca[++cna]=1;
            now=59;
            while(r>2)
            {
                for(int i=now;i>=1;i--)
                {
                    if(r>f[i])
                    {
                        r=cb[++cnb]=r%f[i];
                        now=i;
                        break;
                    }
                }
            }
            if(r==2&&cb[cnb]!=2) cb[++cnb]=2;
    		if(cb[1]!=1) cb[++cnb]=1;
    		int i=1,j=1;
            while(1)
            {
                if(ca[i]==cb[j])
                {
                    ans=ca[i];
                    break;
                }
                if(ca[i]>cb[j]) i++;
                else j++;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    (131.) CF1113B Sasha and Magnetic Machines

    考虑到值域很小,所以枚举值域内每个数即可,时间复杂度是 (mathcal O(a^3))

    膜一发 lyj

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"queue"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 50005
    
    int n,a[MAXN];
    int ans=0,sum=0;
    int cnt[105];
    
    int main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) read(a[i]),cnt[a[i]]++,sum+=a[i];
    	for(int i=1;i<=100;i++)
    	{
    		if(!cnt[i]) continue;
    		for(int j=1;j<=100;j++)
    		{
    			if(!cnt[j]) continue;
    			if(j==i) continue;
    			for(int k=2;k<=i;k++)
    			{
    				if(i%k!=0) continue;
    				ans=max(ans,i-i/k-(j*k-j));
    			}
    		}
    	}
    	printf("%d
    ",sum-ans);
    	return 0;
    }
    

    (132.) P4873 [USACO14DEC]Cow Jog G

    最长不上升子序列问题,要记住带等号,,,,,

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

    #include"algorithm"
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    #define ll long long
    #define read(x) scanf("%d",&x)
    
    int n;
    int t,p,v;
    struct node
    {
    	ll o,r;
    }a[MAXN];
    ll minx=1ll<<62;
    ll rt[MAXN];
    int top=1;
    
    bool cmp(node n,node m){return n.o<m.o;}
    
    int main()
    {
    	read(n),read(t);
    	for(int i=1;i<=n;i++)
    	{
    		read(p),read(v);
    		a[i].o=(ll)p,a[i].r=(ll)p+(ll)t*(ll)v;
    	}
    	sort(a+1,a+n+1,cmp);
    	rt[top]=a[1].r;
    	for(int i=2;i<=n;i++)
    	{
    		if(a[i].r<=rt[top])
    		{
    			rt[++top]=a[i].r;
    			continue;
    		}
    		int l=1,r=top,mid;
    		while(l<r)
    		{
    			mid=(l+r)>>1;
    			if(rt[mid]>=a[i].r) l=mid+1;
    			else r=mid;
    		}
    		rt[l]=a[i].r;
    	}
    	printf("%d
    ",top);
    	return 0;
    }
    

    (133.) P7009 [CERC2013]Magical GCD

    区间 (gcd) 个数只有 (log n) 个就太好了!

    考虑用 (st) 表维护区间最大公约数,二分来确定边界,这个题就有了 (mathcal O(nlog ^3n)) 的神仙解法(所以当然不是我自己想出的/kk)。

    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    #define ll long long
    
    int n,t;
    ll a[MAXN];
    ll st[MAXN][18];
    ll ans=0;
    
    ll gcd(ll a,ll b){return (b==0)?a:gcd(b,a%b);}
    
    ll query(int l,int r)
    {
    	int op=floor(log(r-l+1)/log(2));
    	int len=1<<op;
    	return gcd(st[l][op],st[r-len+1][op]);
    } 
    
    int main()
    {
    	scanf("%d",&t);
    	while(t--)
    	{
    		scanf("%d",&n);
    		int h=floor(log(n)/log(2));
    		ans=0,memset(st,0,sizeof(0));
    		for(int i=1;i<=n;i++) scanf("%lld",&st[i][0]);
    		for(int i=1;i<=h;i++)
    		{
    			int len=1<<i;
    			for(int l=1;l<=(n-len+1);l++)
    			{
    				st[l][i]=gcd(st[l][i-1],st[l+(len>>1)][i-1]);
    			}
    		}
    		for(int l=1;l<=n;l++)
    		{
    			for(int r=l;r<=n;r++)
    			{
    				ll now=query(l,r);
    				int le=1,rr=n-r+1,mid;
    				while(le<rr)
    				{
    					mid=(le+rr)>>1;
    					if(query(l,r-1+mid)==now) le=mid+1;
    					else rr=mid;
    				}
    				if(query(l,r-1+le)!=now) le--;
    				ans=max(ans,now*(ll)(r+le-l));
    				r=le+r-1;
    			}
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    (134.) CF888D Almost Identity Permutations

    这个比较容易看出是一个有关错排的问题,记住错排递推公式:

    [D_i=(i-1)(D_{i-1}+D_{i-2}) ]

    容易发现此题答案是 :

    [sum_{i=0}^k dbinom{n}{i}D_i ]

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 1005
    #define read(x) scanf("%d",&x)
    
    long long c[MAXN][MAXN];
    int D[10],n,k;
    long long ans=0;
    
    int main()
    {
    	read(n),read(k);
    	for(int i=0;i<=n;i++) c[i][0]=1ll;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1];
    	}
    	D[0]=1,D[1]=0,D[2]=1,D[3]=2,D[4]=9;
    	for(int i=0;i<=k;i++)
    	{
    		ans+=(long long)c[n][i]*(long long)D[i];	
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    (135.) P7075 儒略日

    感 谢 C C F 放 送,超 级 大 模 拟。

    不过部分分和数据都很良心的说。

    直接写就完了,这里是无脑二分,时间复杂度是 (mathcal O(Qlog r))

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<map>
    #include<cstring>
    #include<ctime>
    #define ll long long
    #define N number_
    #define M number_
    using namespace std;
    
    #define MAXN 5000005
    #define read(x) scanf("%d",&x)
    
    int q;
    ll x;
    int lsd,lsm,lsy;
    struct node
    {
    	int y,m,d;
    }ans[MAXN];
    
    int main()
    {
    	int d=1,m=1,y=-4713;
    	ans[0].d=1,ans[0].m=1,ans[0].y=-4713;
    	for(int i=1;i<=1721424;i++)
    	{
    		d++;
    		if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)
    		{
    			if(d==32) d=1,m++;
    		}
    		else if(m!=2)
    		{
    			if(d==31) d=1,m++;
    		}
    		else
    		{
    			if((-y)%4==1&&d==30)
    			{
    				d=1,m=3;	
    			}
    			else if((-y)%4!=1&&d==29)
    			{
    				d=1,m=3;
    			}
    		}
    		if(m>12) m=1,d=1,y++;
    		ans[i].d=d,ans[i].y=y,ans[i].m=m;
    	}
    	ans[1721424].y=1;
    	y=1,d=1,m=1;
    	for(int i=1721425;i<=2299160;i++)
    	{
    		d++;
    		if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)
    		{
    			if(d==32) d=1,m++;
    		}
    		else if(m!=2)
    		{
    			if(d==31) d=1,m++;
    		}
    		else
    		{
    			if(y%4==0&&d==30) d=1,m=3;
    			else if(y%4!=0&&d==29) d=1,m=3;
    		}
    		if(m>12) m=1,d=1,y++;
    		ans[i].d=d,ans[i].y=y,ans[i].m=m;
    	}
    	ans[2299161].d=15,ans[2299161].y=1582,ans[2299161].m=10;
    	d=15,m=10,y=1582;
    	for(int i=2299162;i<=2305813;i++)
    	{
    		d++;
    		if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)
    		{
    			if(d==32) d=1,m++;
    		}
    		else if(m!=2)
    		{
    			if(d==31) d=1,m++;
    		}
    		else if(m==2)
    		{
    			if(y%4!=0)
    			{
    				if(d==29) d=1,m=3;
    			}
    			else
    			{
    				if(y%100!=0)
    				{
    					if(d==30) d=1,m=3;
    				}
    				else
    				{
    					if(y%400!=0)
    					{
    						if(d==29) d=1,m=3;
    					}
    					else 
    					{
    						if(d==30) d=1,m=3;
    					}
    				}
    			}
    		}
    		if(m>12) m=1,d=1,y++;
    		ans[i].d=d,ans[i].y=y,ans[i].m=m;
    	}
    	read(q);
    	for(int i=1;i<=q;i++)
    	{
    		scanf("%lld",&x);
    		if(x<=2305813)
    		{
    			printf("%d %d ",ans[x].d,ans[x].m);
    			if(ans[x].y<0)
    			{
    				printf("%d BC
    ",-ans[x].y);
    			}
    			else printf("%d
    ",ans[x].y);
    		}
    		else
    		{
    			x-=2305813;
    			int l=1601,r=1000000001,mid;
    			ll dd;
    			while(l<r)
    			{
    				mid=(l+r)>>1;
    				int fe=(mid-1601);
    				int op=(fe/4)-(fe/100)+(fe/400);
    				dd=(1ll*op*366ll+1ll*(fe-op)*365ll);
    				if(dd>=x) r=mid;
    				else l=mid+1;
    			}
    			int fe=(l-1601);
    			int op=(fe/4)-(fe/100)+(fe/400);
    			dd=(1ll*op*366ll+1ll*(fe-op)*365ll);
    			if(dd>=x) l--;
    			int f=0;
    			m=0,d=0;
    			if(l%400==0||(l%4==0&&l%100!=0)) f=1;
    			fe=(l-1601);
    			op=(fe/4)-(fe/100)+(fe/400);
    			dd=(1ll*op*366ll+1ll*(fe-op)*365ll);
    			int now=0,df=x-dd;
    			for(int j=1;j<=12;j++)
    			{
    				if(j==1||j==3||j==5||j==7||j==8||j==10||j==12)
    				{
    					now+=31;
    					if(df<=now)
    					{
    						df=df-now+31;
    						m=j;
    						break;
    					}
    				}
    				else if(j!=2)
    				{
    					now+=30;
    					if(df<=now)
    					{
    						df=df-now+30;
    						m=j;
    						break;
    					}
    				}
    				else
    				{
    					if(f) now+=29;
    					else now+=28;
    					if(df<=now)
    					{
    						if(f) df=df-now+29;
    						else df=df-now+28;
    						m=j;
    						break;
    					}
    				}	
    			}
    			for(int i=1;;i++)
    			{
    				df--;
    				if(!df)
    				{
    					d=i;
    					break;
    				}
    			}
    			printf("%d %d %d
    ",d,m,l);
    		}
    	}
    	return 0;
    }
    

    (136.) P7076 动物园

    CSP 的签到题,估计就我 CE 了。

    细节很多,比如说特判,左移 (64)位分开处理都很神仙,时间复杂度是 (mathcal O(nk+m))

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<map>
    #include<cstring>
    #include<ctime>
    #define ll long long
    #define dd double
    #define N number_
    #define M number_
    using namespace std;
    
    #define MAXN 1000005
    #define ull unsigned long long
    #define read(x) scanf("%d",&x)
    
    int n,m,c,k;
    ull a[MAXN];
    int cnt[100],vis[100];
    int p,q,tot;
    
    int main()
    {
    	//freopen("zoo.in","r",stdin);
    	//freopen("zoo.out","w",stdout);
    	read(n),read(m),read(c),read(k),tot=k;
    	if(!n&&!m&&k==64) return puts("18446744073709551616"),0;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%llu",&a[i]);
    		for(int j=k-1;j>=0;j--)
    		{
    			if(a[i]&(1ull<<j)) cnt[j]++;
    		}
    	}
    	for(int i=1;i<=m;i++)
    	{
    		read(p),read(q);
    		if(!cnt[p]&&!vis[p])
    		{
    			tot--;
    			vis[p]=1;
    		}
    	}
    	if(tot==64) printf("%llu
    ",(1ull<<63)-(ull)n+(1ull<<63));
    	else printf("%llu
    ",(1ull<<tot)-(ull)n);
    	return 0;
    }
    

    (137.) P3130 [USACO15DEC]haybalesCounting Haybale P

    线段树上二分模板题,时间复杂度是 (mathcal O(mlog n)),没有思维难度。

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 200005
    #define ll long long
    #define read(x) scanf("%d",&x)
    
    struct node
    {
    	int l,r;
    	ll sum,minx,lazy;
    }a[MAXN<<2];
    int n,m,l,r,x;
    int t[MAXN];
    char ty[3];
    
    void update(int k)
    {
    	a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
    	a[k].minx=min(a[k<<1].minx,a[k<<1|1].minx);
    }
    
    void build(int k,int l,int r)
    {
    	a[k].l=l,a[k].r=r;
    	if(l==r){a[k].sum=a[k].minx=(ll)t[l];return;}
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    	update(k);
    }
    
    void lazydown(int k)
    {
    	if(a[k].l==a[k].r){a[k].lazy=0;return;}
    	a[k<<1].minx+=a[k].lazy,a[k<<1|1].minx+=a[k].lazy;
    	a[k<<1].sum+=(a[k<<1].r-a[k<<1].l+1)*1ll*a[k].lazy;
    	a[k<<1|1].sum+=(a[k<<1|1].r-a[k<<1|1].l+1)*1ll*a[k].lazy;
    	a[k<<1].lazy+=a[k].lazy,a[k<<1|1].lazy+=a[k].lazy;
    	a[k].lazy=0;
    }
    
    void modify(int k,int l,int r,ll c)
    {
    	if(a[k].l==l&&a[k].r==r)
    	{
    		a[k].sum+=(a[k].r-a[k].l+1)*1ll*c;
    		a[k].minx+=c;
    		a[k].lazy+=c;
    		return;
    	}
    	if(a[k].lazy) lazydown(k);
    	int mid=(a[k].l+a[k].r)>>1;
    	if(r<=mid) modify(k<<1,l,r,c);
    	else if(l>mid) modify(k<<1|1,l,r,c);
    	else modify(k<<1,l,mid,c),modify(k<<1|1,mid+1,r,c);
    	update(k);
    }
    
    ll Squery(int k,int l,int r)
    {
    	if(a[k].l==l&&a[k].r==r) return a[k].sum;
    	if(a[k].lazy) lazydown(k);
    	int mid=(a[k].l+a[k].r)>>1;
    	if(r<=mid) return Squery(k<<1,l,r);
    	else if(l>mid) return Squery(k<<1|1,l,r);
    	else return Squery(k<<1,l,mid)+Squery(k<<1|1,mid+1,r);
    }
    
    ll Mquery(int k,int l,int r)
    {
    	if(a[k].l==l&&a[k].r==r) return a[k].minx;
    	if(a[k].lazy) lazydown(k);
    	int mid=(a[k].l+a[k].r)>>1;
    	if(r<=mid) return Mquery(k<<1,l,r);
    	else if(l>mid) return Mquery(k<<1|1,l,r);
    	else return min(Mquery(k<<1,l,mid),Mquery(k<<1|1,mid+1,r));
    }
    
    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++)
    	{
    		scanf("%s",ty),read(l),read(r);
    		if(ty[0]=='M') printf("%lld
    ",Mquery(1,l,r));
    		else if(ty[0]=='S') printf("%lld
    ",Squery(1,l,r));
    		else read(x),modify(1,l,r,(ll)x);
    	}
    	return 0;
    }
    

    (138.) P3937 Changing

    这个玩意好像挺经典的。

    我们发现 (t<n) 时,递推式是一个倒金字塔的样子,容易得到:

    [a_{i,j}=a_{i-1,j}; ext{xor};a_{i-1,j+1} ]

    (a_{i,j}) 代表着 (i) 时刻 (j) 位置的值,这时我们容易想到先把数组平移一下,让第 (k) 位到第一位去。

    然后我们拆一下式子发现当层数 (p)(2) 整数次幂时,满足

    [a_{i,j}=a_{i-p,j}; ext{xor}; a_{i-p,j+p} ]

    然后就可以 (mathcal O(nlog n)) 解决这个问题了。

    注意空间不太够,所以滚动数组一下就可以了。

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 3000000
    #define read(x) scanf("%d",&x)
    
    int n,t,k;
    int a[MAXN][2];
    int f=0;
    
    int main()
    {
    	read(n),read(t),read(k);
    	for(int i=1;i<=n;i++) read(a[i][0]);
    	for(int i=k;i<=n;i++) a[i-k+1][1]=a[i][0];
    	for(int i=1;i<k;i++) a[n-k+1+i][1]=a[i][0];
    	if(t==1) return printf("%d
    ",a[1][1]^a[2][1]);
    	for(int i=19;i>=0;i--)
    	{
    		int rt=1<<i;
    		if(rt>t) continue;
    		for(int j=1;j<=n-rt;j++) a[j][f]=a[j][f^1]^a[j+rt][f^1];
    		f^=1,t-=rt;
    	}
    	printf("%d
    ",a[1][f^1]);
    	return 0;
    }
    

    (139.) P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

    线段树合并模板题,可以类似树上差分的来维护合并,当然还有一种两个 (log) 的神仙做法,不太会。

    线段树合并看起来很暴力,但出于修改的限制,时间复杂度是严格的 (mathcal O(mlog n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    #define read(x) scanf("%d",&x)
    
    int n,m,u,v,c;
    int dep[MAXN],top[MAXN],f[MAXN],son[MAXN],tot[MAXN];
    struct node
    {
    	int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    struct Tree
    {
    	int ls,rs,maxn,pos;
    }a[MAXN*55];
    int opt=0,root[MAXN];
    int ans[MAXN];
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    int dfs1(int cur,int fa)
    {
    	f[cur]=fa,dep[cur]=dep[fa]+1,tot[cur]=1;
    	int maxn=0;
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==fa) continue;
    		int op=dfs1(j,cur);
    		tot[cur]+=op;
    		if(maxn<op) maxn=op,son[cur]=j;
    	}
    	return tot[cur];
    }
    
    void dfs2(int cur,int topf)
    {
    	top[cur]=topf;
    	if(son[cur]) dfs2(son[cur],topf);
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==f[cur]||j==son[cur]) continue;
    		dfs2(j,j);
    	}
    }
    
    int LCA(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		u=f[top[u]];
    	}
    	return (dep[u]<dep[v])?u:v;
    }
    
    void update(int k)
    {
    	if(a[a[k].ls].maxn>=a[a[k].rs].maxn)
    		a[k].maxn=a[a[k].ls].maxn,a[k].pos=a[a[k].ls].pos;
    	else
    		a[k].maxn=a[a[k].rs].maxn,a[k].pos=a[a[k].rs].pos;
    }
    
    int modify(int &k,int l,int r,int x,int y)
    {
    	if(!k) k=++opt;
    	if(l==r)
    	{
    		a[k].maxn+=y,a[k].pos=l;
    		return k;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) a[k].ls=modify(a[k].ls,l,mid,x,y);
    	else a[k].rs=modify(a[k].rs,mid+1,r,x,y);
    	update(k);
    	return k;
    }
    
    int merge(int u,int v,int l,int r)
    {
    	if(!u||!v) return u|v;
    	if(l==r) 
    	{
    		a[u].maxn+=a[v].maxn;
    		a[u].pos=l;
    		return u;
    	}
    	int mid=(l+r)>>1;
    	a[u].ls=merge(a[u].ls,a[v].ls,l,mid);
    	a[u].rs=merge(a[u].rs,a[v].rs,mid+1,r);
    	update(u);
    	return u;
    }
    
    void dfs(int cur)
    {
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==f[cur]) continue;
    		dfs(j);
    		root[cur]=merge(root[cur],root[j],1,100000);
    	}
    	if(a[root[cur]].maxn) ans[cur]=a[root[cur]].pos;
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
    	dfs1(1,1),dfs2(1,1);
    	for(int i=1;i<=m;i++)
    	{
    		read(u),read(v),read(c);
    		int now=LCA(u,v);
    		root[u]=modify(root[u],1,100000,c,1);
    		root[v]=modify(root[v],1,100000,c,1);
    		root[now]=modify(root[now],1,100000,c,-1);
    		if(f[now]^now) root[f[now]]=modify(root[f[now]],1,100000,c,-1);
    	}
    	dfs(1);
    	for(int i=1;i<=n;i++) printf("%d
    ",ans[i]);
    	return 0;
    }
    

    (140.) P2832 行路难

    bug 题,错误思路反向建边就能过,不知道为什么,思路就是复合加权的最短路问题了。

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"queue"
    using namespace std;
    
    #define read(x) scanf("%lld",&x)
    #define int long long
    
    int n,m;
    int u,v,w;
    struct node
    {
    	int to,nxt,w;
    }e[200005];
    int head[10005],cnt=0;
    int dis[10005],len[10005],vis[10005],pre[10005];
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
    
    void add(int u,int v,int w){e[++cnt].nxt=head[u],e[cnt].to=v,e[cnt].w=w,head[u]=cnt;}
    
    void dij()
    {
    	while(!q.empty())
    	{
    		int u=q.top().second;
    		q.pop();
    		if(vis[u]) continue;
    		vis[u]=1;
    		for(int i=head[u];i;i=e[i].nxt)
    		{
    			int j=e[i].to;
    			if(dis[j]>dis[u]+len[u]+1ll+e[i].w)
    			{
    				dis[j]=dis[u]+len[u]+1ll+e[i].w;
    				len[j]=len[u]+1ll,pre[j]=u;
    				q.push(make_pair(dis[j],j));
    			}
    		}
    	}
    	return;
    }
    
    
    signed main()
    {
    	read(n),read(m);
    	for(int i=1;i<=m;i++) read(u),read(v),read(w),add(v,u,w);
    	for(int i=1;i<=n;i++) dis[i]=1ll<<62,len[i]=-1ll;
    	dis[n]=0,q.push(make_pair(0,n));
    	dij();
    	printf("%lld
    ",dis[1]);
    	int p=1;
    	while(p)
    	{
    		printf("%lld ",p);
    		p=pre[p];
    	}
    	return puts(""),0;
    }
    

    (141.) P3521 [POI2011]ROT-Tree Rotations

    线段树合并练习题。

    考虑按树形结构合并,容易想到只有两种方法,这时候分别维护左右儿子谁在前的的逆序对新加数,具体就是左右子树大小之积。

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 200005
    #define ll long long
    #define read(x) scanf("%d",&x)
    
    int n,p;
    struct node
    {
    	int ls,rs,sum;
    	node(){ls=rs=sum=0;}
    }a[MAXN*25];
    int cnt=0,op=0,root[MAXN];
    ll ans=0,now1=0,now2=0;
    
    inline void update(int k){a[k].sum=a[a[k].ls].sum+a[a[k].rs].sum;}
    
    int build(int k,int l,int r,int x)
    {
    	if(!k) k=++cnt;
    	if(l==r){a[k].sum=1;return k;}
    	int mid=(l+r)>>1;
    	if(x<=mid) a[k].ls=build(a[k].ls,l,mid,x);
    	else a[k].rs=build(a[k].rs,mid+1,r,x);
    	update(k);
    	return k;
    }
    
    int merge(int u,int v,int l,int r)
    {
    	if(!u||!v) return u|v;
    	if(l==r){a[u].sum+=a[v].sum;return u;}
    	int mid=(l+r)>>1;
    	now1+=1ll*a[a[u].ls].sum*a[a[v].rs].sum;
    	now2+=1ll*a[a[v].ls].sum*a[a[u].rs].sum;
    	a[u].ls=merge(a[u].ls,a[v].ls,l,mid);
    	a[u].rs=merge(a[u].rs,a[v].rs,mid+1,r);
    	update(u);
    	return u;
    }
    
    int work()
    {
    	read(p);
    	int er=p;
    	if(er) 
    	{
    		op++;
    		root[op]=build(root[op],1,n,er);
    		return op;
    	}
    	else
    	{
    		int lso=work();
    		int rso=work();
    		root[lso]=merge(root[lso],root[rso],1,n);
    		ans+=min(now1,now2);
    		now1=now2=0;
    		return lso;
    	}
    }
    
    int main()
    {
    	read(n);
    	work();
    	return printf("%lld
    ",ans),0;
    }
    

    (141.) CF600E Lomsat gelral

    对每个节点分别开权值线段树,然后进行合并即可。

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    #define read(x) scanf("%lld",&x)
    #define int long long
    
    int n,u,v;
    struct edge
    {
    	int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    struct node
    {
    	int ls,rs,sum,maxn;
    }a[MAXN*25];
    int opt=0,rt[MAXN],ans[MAXN];
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    void update(int k)
    {
    	if(a[a[k].ls].maxn==a[a[k].rs].maxn)
    		a[k].maxn=a[a[k].ls].maxn,a[k].sum=a[a[k].ls].sum+a[a[k].rs].sum;
    	else if(a[a[k].ls].maxn>a[a[k].rs].maxn)
    		a[k].maxn=a[a[k].ls].maxn,a[k].sum=a[a[k].ls].sum;
    	else
    		a[k].maxn=a[a[k].rs].maxn,a[k].sum=a[a[k].rs].sum;
    }
    
    int build(int k,int l,int r,int x)
    {
    	if(!k) k=++opt;
    	if(l==r)
    	{
    		a[k].sum=x,a[k].maxn=1;
    		return k;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) a[k].ls=build(a[k].ls,l,mid,x);
    	else a[k].rs=build(a[k].rs,mid+1,r,x);
    	return update(k),k;
    }
    
    int merge(int u,int v,int l,int r)
    {
    	if(!u||!v) return u|v;
    	if(l==r) 
    	{
    		a[u].maxn+=a[v].maxn;
    		return u;
    	}
    	int mid=(l+r)>>1;
    	a[u].ls=merge(a[u].ls,a[v].ls,l,mid);
    	a[u].rs=merge(a[u].rs,a[v].rs,mid+1,r);
    	return update(u),u;
    }
    
    void dfs(int cur,int fa)
    {
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(j==fa) continue;
    		dfs(j,cur);
    		rt[cur]=merge(rt[cur],rt[j],1,100000);
    	}
    	ans[cur]=a[rt[cur]].sum;
    }
    
    signed main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) read(u),rt[i]=build(rt[i],1,100000,u);
    	for(int i=1;i<n;i++)
    	{
    		read(u),read(v);
    		add(u,v),add(v,u);
    	}	
    	dfs(1,1);
    	for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
    	return puts(""),0;
    }
    

    (142.) P3434 [POI2006]KRA-The Disks

    果然是数据结构中毒选手,果断线段树上二分。

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 300005
    #define read(x) scanf("%d",&x)
    
    int n,m;
    struct node
    {
    	int l,r,minx;
    }a[MAXN<<2];
    int t[MAXN],x,R;
    
    inline void update(int k){a[k].minx=min(a[k<<1].minx,a[k<<1|1].minx);}
    
    inline void build(int k,int l,int r)
    {
    	a[k].l=l,a[k].r=r;
    	if(l==r){a[k].minx=t[l];return;}
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    	update(k);
    }
    
    inline int query(int k,int l,int r,int x)
    {
    	if(a[k].l==a[k].r)
    	{
    		if(a[k].l==1&&x>a[k].minx) return 0;
    		return a[k].l;
    	}
    	int mid=(a[k].l+a[k].r)>>1;
    	if(r<=mid) return query(k<<1,l,r,x);
    	else if(a[k<<1].minx<x) return query(k<<1,l,mid,x);
    	else return query(k<<1|1,mid+1,r,x);
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(t[i]);
    	build(1,1,n),R=n;
    	for(int i=1;i<=m;i++)
    	{
    		read(x);
    		if(R<0) continue;
    		R=query(1,1,R,x)-1;
    	}	
    	if(R<0) puts("0");
    	else printf("%d
    ",R);
    	return 0;
    }
    

    (143.) P7078 贪吃蛇

    具体见这里:CSP 2020 S 题解

    这里只放一下代码:

    #include"cstring"
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"queue"
    using namespace std;
    
    #define MAXN 1000005
    #define int long long
    #define read(x) scanf("%d",&x)
    
    int t,n,m,x,y;
    int a[MAXN],b[MAXN],tt[MAXN],vis[MAXN];
    int l,r;
    int head=0,tail=1;
    struct node
    {
    	int val,id;
    }q[MAXN*2];
    int maxn,minx,minxx;
    int cnt,tot;
    int num1,num2;
    
    signed main()
    {
    	read(t),t--;
    	read(n),l=1,r=n,cnt=n;
    	for(int i=1;i<=n;i++) read(a[i]),b[i]=i,tt[i]=a[i];
    	while(1)
    	{
    		if(head>=tail)
    		{
    			if(q[tail].val>a[r]||(q[tail].val==a[r]&&q[tail].id>b[r]))
    				num1=q[tail].id,maxn=q[tail++].val;
    			else num1=b[r],maxn=a[r--];
    			if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    				minx=q[head--].val;
    			else minx=a[l++];
    		}
    		else num1=b[r],maxn=a[r--],minx=a[l++];
    		cnt-=2;
    		if(!cnt)
    		{
    			puts("1");
    			break;
    		}
    		if(head>=tail)
    		{
    			if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    				num2=q[head].id,minxx=q[head].val;
    			else num2=b[l],minxx=a[l];
    		}
    		else num2=b[l],minxx=a[l];
    		if(maxn-minx<minxx||(maxn-minx==minxx&&num1<num2))
    		{
    			tot=cnt+1;
    			a[--l]=maxn-minx,b[l]=num1;
    			int op=0;
    			while(1)
    			{
    				if(head>=tail)
    				{
    					if(q[tail].val>a[r]||(q[tail].val==a[r]&&q[tail].id>b[r]))
    						num1=q[tail].id,maxn=q[tail++].val;
    					else num1=b[r],maxn=a[r--];
    					if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    						minx=q[head--].val;
    					else minx=a[l++];
    				}
    				else num1=b[r],maxn=a[r--],minx=a[l++];
    				tot-=2;
    				if(!tot)
    				{
    					if(op%2==1) cnt--;
    					break;
    				}
    				if(head>=tail)
    				{
    					if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    						num2=q[head].id,minxx=q[head].val;
    					else num2=b[l],minxx=a[l];
    				}
    				else num2=b[l],minxx=a[l];
    				if(maxn-minx>minxx||(maxn-minx==minxx&&num1>num2))
    				{
    					if(op%2==1) cnt--;
    					break;
    				}
    				op++,tot++;
    				a[--l]=maxn-minx,b[l]=num1;
    			}
    			printf("%d
    ",cnt+2);
    			break;
    		}
    		else if(maxn-minx>a[r]||(maxn-minx==a[r]&&num1>b[r]))
    		{
    			a[++r]=maxn-minx;
    			b[r]=num1;
    		}
    		else q[++head].val=maxn-minx,q[head].id=num1;
    		cnt++;
    	}
    	while(t--)
    	{
    		l=1,r=n,cnt=n;
    		head=0,tail=1;
    		memset(q,0,sizeof(q));
    		memset(vis,0,sizeof(vis));
    		read(m);
    		for(int i=1;i<=m;i++)
    		{
    			read(x),read(y);
    			a[x]=y,vis[x]=1;
    		}
    		for(int i=1;i<=n;i++)
    		{
    			b[i]=i;
    			if(!vis[i]) a[i]=tt[i];
    			tt[i]=a[i];
    		}
    		while(1)
    		{
    			if(head>=tail)
    			{
    				if(q[tail].val>a[r]||(q[tail].val==a[r]&&q[tail].id>b[r]))
    					num1=q[tail].id,maxn=q[tail++].val;
    				else num1=b[r],maxn=a[r--];
    				if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    					minx=q[head--].val;
    				else minx=a[l++];
    			}
    			else num1=b[r],maxn=a[r--],minx=a[l++];
    			cnt-=2;
    			if(!cnt)
    			{
    				puts("1");
    				break;
    			}
    			if(head>=tail)
    			{
    				if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    					num2=q[head].id,minxx=q[head].val;
    				else num2=b[l],minxx=a[l];
    			}
    			else num2=b[l],minxx=a[l];
    			if(maxn-minx<minxx||(maxn-minx==minxx&&num1<num2))
    			{
    				tot=cnt+1;
    				a[--l]=maxn-minx,b[l]=num1;
    				int op=0;
    				while(1)
    				{
    					if(head>=tail)
    					{
    						if(q[tail].val>a[r]||(q[tail].val==a[r]&&q[tail].id>b[r]))
    							num1=q[tail].id,maxn=q[tail++].val;
    						else num1=b[r],maxn=a[r--];
    						if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    							minx=q[head--].val;
    						else minx=a[l++];
    					}
    					else num1=b[r],maxn=a[r--],minx=a[l++];
    					tot-=2;
    					if(!tot)
    					{
    						if(op%2==1) cnt--;
    						break;
    					}
    					if(head>=tail)
    					{
    						if(q[head].val<a[l]||(q[head].val==a[l]&&q[head].id<b[l]))
    							num2=q[head].id,minxx=q[head].val;
    						else num2=b[l],minxx=a[l];
    					}
    					else num2=b[l],minxx=a[l];
    					if(maxn-minx>minxx||(maxn-minx==minxx&&num1>num2))
    					{
    						if(op%2==1) cnt--;
    						break;
    					}
    					op++,tot++;
    					a[--l]=maxn-minx,b[l]=num1;
    				}
    				printf("%d
    ",cnt+2);
    				break;
    			}
    			else if(maxn-minx>a[r]||(maxn-minx==a[r]&&num1>b[r]))
    			{
    				a[++r]=maxn-minx;
    				b[r]=num1;
    			}
    			else q[++head].val=maxn-minx,q[head].id=num1;
    			cnt++;
    		}
    	}
    	return 0;
    }
    

    (144.) P7077 函数调用

    还是看哪个题解吧,,,,

    #include"iostream"
    #include"cstdio"
    #include"queue"
    using namespace std;
    
    #define int long long 
    #define read(x) scanf("%lld",&x)
    #define MAXN 100005
    #define MOD 998244353
    
    int n,m,q;
    int c,x,a[MAXN];
    struct node
    {
    	int to,nxt;
    }e[MAXN*10];
    int head[MAXN],cnt=0;
    int mul[MAXN],f[MAXN];
    int ty[MAXN],pos[MAXN],fac[MAXN];
    int b[MAXN],deg[MAXN],dp[MAXN],ad[MAXN];
    int vis[MAXN];
    queue<int>qu;
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    void dfs(int cur)
    {
    	vis[cur]=mul[cur]=1ll;
    	if(ty[cur]==2) mul[cur]=fac[cur];
    	for(int i=head[cur];i;i=e[i].nxt)
    	{
    		int j=e[i].to;
    		if(!vis[j]) dfs(j);
    		mul[cur]=mul[cur]*mul[j]%MOD;
    	}
    	return;
    }
    
    signed main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) read(a[i]);
    	read(m);
    	for(int i=1;i<=m;i++)
    	{
    		read(ty[i]);
    		if(ty[i]==1) read(pos[i]),read(fac[i]);
    		else if(ty[i]==2) read(fac[i]);
    		else
    		{
    			read(c);
    			for(int j=1;j<=c;j++) read(x),add(i,x),deg[x]++;
    		}
    	}
    	for(int i=1;i<=m;i++) if(!vis[i]) dfs(i);
    	read(q);
    	for(int i=1;i<=q;i++) read(b[i]);
    	f[q+1]=1ll,dp[b[q]]=1ll;
    	for(int i=q;i>=1;i--)
    	{
    		f[i]=f[i+1]*mul[b[i]]%MOD;
    		dp[b[i-1]]=(dp[b[i-1]]+f[i])%MOD; 
    	}
    	for(int i=1;i<=m;i++) if(!deg[i]) qu.push(i);
    	while(!qu.empty())
    	{
    		int u=qu.front();
    		qu.pop();
    		if(ty[u]==1) ad[pos[u]]=(ad[pos[u]]+fac[u]*dp[u]%MOD)%MOD;
    		int mll=1ll;
    		for(int i=head[u];i;i=e[i].nxt)
    		{
    			int j=e[i].to;
    			dp[j]=(dp[j]+mll*dp[u]%MOD)%MOD;
    			mll=mll*mul[j]%MOD;
    			deg[j]--;
    			if(!deg[j]) qu.push(j);
    		}
    	}
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=a[i]*f[1]%MOD+ad[i];
    		printf("%lld ",a[i]%MOD);
    	}
    	return puts(""),0;
    }
    

    (145.) P2125 图书馆书架上的书

    环形均分纸牌问题。

    考虑记 (x_i)(isim i+1) 的数量,特殊的,(x_n)(nsim 1) 的。

    容易有:

    [x_i=x_{i-1}-(overline{a}-a_i) ]

    把所有的数都用 (x_1) 表示,最后求 (sumlimits_{i=1}^n |x_i|) 就只剩一一个参数了,然后中位数定理即可。

    实现的好的话应该可以 (mathcal O(n)),我比较懒,所以写的是好写的 (mathcal O(nlog n))

    #include"algorithm"
    #include"iostream"
    #include"cstdio"
    using namespace std;
    
    #define ll long long 
    #define MAXN 5000005
    
    int n;
    ll a[MAXN],s[MAXN],c[MAXN],p[MAXN];
    ll x[MAXN];
    ll sum=0,now;
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum+=a[i];
    	sum=sum/(ll)n;
    	for(int i=1;i<=n;i++) s[i]=sum-a[i];
    	for(int i=2;i<=n;i++) c[i]=c[i-1]+s[i],p[i]=c[i];
    	sort(c+1,c+n+1),sum=0;
    	now=c[(n+1)/2];
    	for(int i=1;i<=n;i++) sum=sum+abs(now-p[i]);
    	printf("%lld
    ",sum);
    	for(int i=1;i<=n;i++) x[i]=now-p[i];
    	printf("%lld %lld
    ",-x[n],x[1]);
    	for(int i=2;i<=n;i++) printf("%lld %lld
    ",-x[i-1],x[i]);
    	return 0;
    }
    

    (146.) P4016 负载平衡问题

    双倍经验啊,,,

    #include"algorithm"
    #include"iostream"
    #include"cstdio"
    using namespace std;
    
    #define ll long long 
    #define MAXN 5000005
    
    int n;
    ll a[MAXN],s[MAXN],c[MAXN],p[MAXN];
    ll x[MAXN];
    ll sum=0,now;
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum+=a[i];
    	sum=sum/(ll)n;
    	for(int i=1;i<=n;i++) s[i]=sum-a[i];
    	for(int i=2;i<=n;i++) c[i]=c[i-1]+s[i],p[i]=c[i];
    	sort(c+1,c+n+1),sum=0;
    	now=c[(n+1)/2];
    	for(int i=1;i<=n;i++) sum=sum+abs(now-p[i]);
    	printf("%lld
    ",sum);
    	return 0;
    }
    

    (147.) P4138 [JOISC2014]挂饰

    我们设设 (dp_{i,j}) 表示考虑前 (i) 个物品,当前剩下不少于 (j) 个空挂钩时挂饰总和的最大值是多少。

    于是有 :

    [dp_{i,j}=max{dp_{i-1,j},dp_{i-1,max{j-a_i,0}+1}} ]

    然后按提供的挂钩数排序即可。

    时间复杂度是 (mathcal O(n^2)).

    #include"algorithm"
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define int long long
    #define MAXN 2005
    #define read(x) scanf("%lld",&x)
    #define inf 1ll<<61
    
    int n;
    struct node
    {
    	int a,b;
    }x[MAXN];
    int dp[MAXN][MAXN];
    int ans=0;
    
    bool cmp(node n,node m){return n.a>m.a;}
    
    signed main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) read(x[i].a),read(x[i].b);
    	sort(x+1,x+n+1,cmp);
    	for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) dp[i][j]=-(inf<<1);
    	dp[0][1]=0;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=0;j+1-x[i].a<=n&&j<=n;j++)
    		{
    			if(dp[i-1][j]>-inf) dp[i][j]=dp[i-1][j];
    			if(dp[i-1][j+1-x[i].a]>-inf)
    				dp[i][j]=max(dp[i][j],dp[i-1][max(0ll,j-x[i].a)+1]+x[i].b);
    			ans=max(ans,dp[i][j]);
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    (148.) P4085 [USACO17DEC]Haybale Feast G

    二分这个阈值,枚举左端点,发现再二分一次右端点就行了。

    第二次二分发现可以线段树,线段树 nm,前缀和维护不就好了/jk

    考虑优化,我们发现可以先处理出值大于这个阈值的位置,然后这个序列就被分成 (k) 个区间,扫一遍取最值就好了,复杂度是 (mathcal O(nlog n))

    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    #define ll long long
    
    int n;
    ll m;
    int f,s[MAXN];
    ll sum[MAXN];
    int l=1000000000,r;
    int op[MAXN],cnt=0;
    
    #define g() getchar()
    
    inline int read()
    {
    	int x=0;
    	char c=g();
    	while(c<'0'||c>'9') c=g();
    	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=g();
    	return x;
    }
    
    inline bool check(int x)
    {
    	memset(op,0,sizeof(op)),cnt=0;
    	for(int i=1;i<=n;i++) if(s[i]>x) op[++cnt]=i;
    	op[++cnt]=n+1;
    	for(register int i=0;i<cnt;i++) 
    	{
    		if(op[i+1]==op[i]+1) continue;
    	    if(sum[op[i+1]-1]-sum[op[i]]>=m) return true;
    	}
    	return false;
    }
    
    int main()
    {
    	n=read(),scanf("%lld",&m);
    	for(register int i=1;i<=n;i++)
    	{
    		f=read(),s[i]=read();
    		sum[i]=sum[i-1]+(ll)f,r=max(r,s[i]),l=min(l,s[i]);
    	}
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid)) r=mid;
    		else l=mid+1;
    	}
    	printf("%d
    ",l);
    	return 0;
    }
    

    (149.) P4799 [CEOI2015 Day2]世界冰球锦标赛

    折半搜索(Meet in Middle) 板子题,预处理一部分,然后再算另一部分,二分匹配即可。

    时间复杂度是 (mathcal O(2^{frac{n}{2}+1}n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    #include"algorithm"
    #include"stack"
    #include"queue"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define readl(x) scanf("%lld",&x)
    #define ll long long
    #define ull unsigned long long
    #define MOD 1000000007
    
    int n;
    ll a[45],ans=0,m;
    ll val[2000005],top=0;
    
    int find(ll x)//注意 long long
    {
    	int l=1,r=top;
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(val[mid]>x) r=mid;
    		else l=mid+1;
    	}
    	if(val[l]>x) l--;
    	return l;
    }
    
    int main()
    {
    	read(n),readl(m);
    	for(int i=1;i<=n;i++) readl(a[i]);
    	int mid=n/2,rt=n-mid;
    	for(int s=0;s<(1<<rt);s++)
    	{
    		ll op=0;
    		for(int i=0;i<rt;i++)
    		{
    			if((1<<i)&s) op+=a[mid+i+1];
    		}
    		val[++top]=op;
    	}
    	sort(val+1,val+top+1);
    	for(int s=0;s<(1<<mid);s++)
    	{
    		ll op=0;
    		for(int i=0;i<mid;i++)
    		{
    			if((1<<i)&s) op+=a[i+1];
    		}
    		if(op<=m) ans+=(ll)find(m-op);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    (150.) P3224 [HNOI2012]永无乡

    线段树合并,并查集辅助维护,这是套路题啊(

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

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"cstring"
    #include"algorithm"
    #include"stack"
    #include"queue"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define readl(x) scanf("%lld",&x)
    #define ll long long
    #define ull unsigned long long
    #define MAXN 100005
    #define MOD 1000000007
    
    int n,m,q,cnt=0;
    struct node
    {
    	int ls,rs,sum,pos;
    	node(){ls=rs=sum=pos;}
    }a[MAXN*28];
    int fa[MAXN],x,y,op,root[MAXN],si[MAXN];
    char c[2];
    
    int getf(int u){return (fa[u]==u)?u:fa[u]=getf(fa[u]);}
    
    void update(int k){a[k].sum=a[a[k].ls].sum+a[a[k].rs].sum;}
    
    int build(int k,int l,int r,int x,int t)
    {
    	if(!k) k=++cnt;
    	if(l==r) 
    	{
    		a[k].sum=1,a[k].pos=t;
    		return k;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) a[k].ls=build(a[k].ls,l,mid,x,t);
    	else a[k].rs=build(a[k].rs,mid+1,r,x,t);
    	return update(k),k;
    }
    
    int merge(int u,int v,int l,int r)
    {
    	if(!u||!v) return u|v;
    	if(l==r)
    	{
    		a[u].sum+=a[v].sum,a[u].pos=a[v].pos|a[u].pos;
    		return u;
    	}
    	int mid=(l+r)>>1;
    	a[u].ls=merge(a[u].ls,a[v].ls,l,mid);
    	a[u].rs=merge(a[u].rs,a[v].rs,mid+1,r);
    	return update(u),u;
    }
    
    int query(int k,int l,int r,int x)
    {
    	if(l==r) return a[k].pos;
    	int midd=a[a[k].ls].sum,mid=(l+r)>>1;
    	if(x<=midd) return query(a[k].ls,l,mid,x);
    	else return query(a[k].rs,mid+1,r,x-midd);
    }
    
    void att(int u,int v)
    {
    	int l=getf(u),r=getf(v);
    	if(l==r) return;
    	else
    	{
    		if(si[r]>si[l]) swap(l,r);
    		fa[r]=l,si[l]+=si[r];
    		root[l]=merge(root[l],root[r],1,n);
    	}
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) fa[i]=i,si[i]=1,read(x),root[i]=build(root[i],1,n,x,i);
    	for(int i=1;i<=m;i++)
    	{
    		read(x),read(y);
    		att(x,y);
    	}
    	read(q);
    	for(int i=1;i<=q;i++)
    	{
    		scanf("%s",c),read(x),read(y);
    		if(c[0]=='Q')
    		{
    			x=getf(x);
    			if(a[root[x]].sum<y) puts("-1");
    			else printf("%d
    ",query(root[x],1,n,y));
    		}
    		else att(x,y);
    	}
    	return 0;
    }
    

    精彩的延续:Link

  • 相关阅读:
    关于技术的学习及批判 人工智能
    爱迪生如何看待手机程序员怎么用移动互联网 人工智能
    量子学习及思考4群体意识 人工智能
    程序员的出路 人工智能
    函数式编程学习之路(14) 人工智能
    量子学习及思考1开篇 人工智能
    函数式编程学习之路(16)图灵完备 人工智能
    开机启动
    动态连接库
    静态常量的问题
  • 原文地址:https://www.cnblogs.com/tlx-blog/p/13900267.html
Copyright © 2011-2022 走看看