• 雅礼学习10.6


    雅礼学习10.6

    上午考试

    各题状况

    T1

    二分答案

    应该有反例,就是说,答案应该不是单调的

    但是不会写其他的算法了啊。。。

    T2

    我TM。。。

    图片.png

    第二个红框圈出来的部分应该是

    if(x1+x1!=s)
    

    写错了,就没了(18)分。。

    T3

    写了个(n^4)的暴力

    最后发现题目中的矩形的四个顶点不一定是给定的顶点。。

    那就GG了

    各题题目及考场代码

    T1

    图片.png

    图片.png

    /*
     * 二分答案。。
     * 复杂度O(20(N+NlogN+M))的,感觉很悬
     * 排序应该可以优化掉,但是不太会哎。
     */
    #include <cstdio>
    #include <algorithm>
    
    inline int read()
    {
    	int n=0,w=1;register char c=getchar();
    	while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
    	return n*w;
    }
    
    const int N=1e6+1;
    int n,m;
    struct Seg{
    	int b,k;
    }seg[N];
    long long S,emp[N];
    
    inline bool judge(long long x)
    {
    	for(int i=1;i<=n;++i)
    		emp[i]=seg[i].k*x+seg[i].b;
    	std::sort(emp+1,emp+1+n);
    /*
    	for(int i=1;i<=n;++i)
    		printf("%lld ",emp[i]);
    	puts("");
    */
    	long long res=0;
    	for(int i=0;i<m;++i)
    	{
    		if(emp[n-i]<0)break;
    		res+=emp[n-i];
    	}
    //	printf("%lld
    ",res);
    	return res>=S;
    }
    
    int main()
    {
    	freopen("merchant.in","r",stdin);
    	freopen("merchant.out","w",stdout);
    
    	n=read(),m=read();
    	scanf("%lld",&S);
    //	printf("%d %d %lld
    ",n,m,S);
    	for(int i=1;i<=n;++i)
    	{
    		seg[i].k=read(),seg[i].b=read();
    //		printf("%d %d
    ",seg[i].k,seg[i].b);
    	}
    	int l=0,r=1e9,mid;
    	while(l<=r)
    	{
    		mid=l+r>>1;
    		if(judge(mid))
    			r=mid-1;
    		else	l=mid+1;
    	}
    	printf("%d",l);
    
    	fclose(stdin),fclose(stdout);
    	return 0;
    }
    

    T2

    图片.png
    图片.png

    /*
     * 题目描述什么鬼啊。。。完全看不懂
     *
     * emmmm
     * 高斯消元么。。。
     */
    #include <cstdio>
    #include <algorithm>
    inline int read()
    {
    	int n=0,w=1;register char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar();
    	return n*w;
    }
    const int N=1e6+1;
    int n,q,fa[N],w[N];
    /*
    const double eps=1e-8;
    double a[N][N];
    
    int sign(double x)
    {//判断一个数是正数、负数还是0
        if(fabs(x)<=eps)return 0;//fabs是对浮点数取绝对值
        if(x>0)return 1;
        return -1;
    }
    
    inline void GG(int x)
    {
    	if(a[x][n+1])
    		puts("none");
    	else	puts("inf");
    }
    
    inline void solve()
    {
    	for(int i=1;i<=n;++i)
    	    a[i][n+1]=b[i];//得到扩展矩阵
    	
    	for(int i=1;i<=n;++i)
    	{
    		int p=i;
    		for(int j=i+1;j<=n;++j)
    			if(fabs(a[j][i]>fabs(a[p][i])))
    				p=j;
    		for(int j=1;j<=n+1;++j)
    			swap(a[p][j],a[i][j]);
    		if(sign(a[i][i])==0)
    			GG(i);
    		for(int j=i+1;j<=n;++j)
    		{
    			double ratio=a[j][i]/a[i][i];//计算是多少倍
    			for(int k=1;k<=n+1;++k)
    				a[j][k]=a[j][k]-ratio*a[i][k];
    		}
    		for(int i=n;i>0;--i)
    		{
    			for(int j=i+1;j<=n;++j)
    				a[i][n+1]=a[i][n+1]-x[j]*a[i][j];
    			x[i]=a[i][n+1]/a[i][i];
    		}
    }
    
    int main()
    {
    	freopen("equation.in","r",stdin);
    	freopen("equation.out","w",stdout);
    	n=read(),q=read();
    	for(int i=2;i<=n;++i)
    		fa[i]=read(),w[i]=read();
    	int type,u,v,s;
    	while(q--)
    	{
    		type=read(),u=read(),v=read();
    		if(type==1)
    		{
    			s=read();
    			solve();
    		}
    		else
    		{
    			w[u]=v;
    			a[u][fa[u]]=v;
    		}
    	}
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    日。。
    写挂了
    */
    int main()
    {
    	freopen("equation.in","r",stdin);
    	freopen("equation.out","w",stdout);
    	n=read(),q=read();
    	for(int i=2;i<=n;++i)
    		fa[i]=read(),w[i]=read();
    	int type,u,v,s;
    	int x2,x1;
    	if(n==2)
    	{
    		while(q--)
    		{
    			type=read(),u=read(),v=read();
    			if(type==1)
    			{
    				s=read();
    				if(u==v && v==2)
    				{
    					x2=s/2;
    					if(x2+x2!=s)
    						puts("none");
    					else	printf("%d
    ",w[2]-x2);
    				}
    				else
    					if(u==v && v==1)
    					{
    						x1=s/2;
    						if(x2+x2!=s)
    							puts("none");
    						else	printf("%d
    ",x1);
    					}
    					else
    					{
    						if(s==w[2])
    							puts("inf");
    						else	puts("none");
    					}
    			}
    			else	w[u]=v;
    		}
    	}
    	else
    	{
    		while(q--)
    		{
    			srand(19260817);
    			while(q--)
    			{
    				int x=rand();
    				if(x%3==1)
    					puts("none");
    				else
    					if(x%3==2)
    						puts("inf");
    					else	printf("%d
    ",x);
    			}
    		}
    	}
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

    T3

    图片.png
    图片.png

    #include <cstdio>
    #include <map>
    
    const int N=2501,mod=1e9+7;
    std::map<int,bool> mp1,mp2;
    int n,t1,t2,lx,ly,rx,ry;
    int ans,x[N],y[N];
    
    inline int read()
    {
    	int n=0,w=1;register char c=getchar();
    	while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
    	return n*w;
    }
    
    inline int min(int x,int y)
    {return x<y?x:y;}
    inline int max(int x,int y)
    {return x>y?x:y;}
    
    int main()
    {
        freopen("rectangle.in","r",stdin);
    	freopen("rectangle.out","w",stdout);
    	n=read();
        for(int i=1;i<=n;++i)
            x[i]=read(),y[i]=read();
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                for(int q=1;q<=n;++q)
                    for(int p=1;p<=n;++p)
    				{
                        if(i==j&&j==q&&q==p)continue;
                        lx=min(min(x[i],x[j]),min(x[q],x[p]));
                        ly=min(min(y[i],y[j]),min(y[q],y[p]));
                        rx=max(max(x[i],x[j]),max(x[q],x[p]));
                        ry=max(max(y[i],y[j]),max(y[q],y[p]));
                        t1=(lx*ry+ly*rx);
                        t2=(lx*rx+ly*ry);
                        if(mp1[t1]==0&&mp2[t2]==0)
    					{
                            ans=(ans+(rx-lx)*(ry-ly))%mod;
                            mp1[t1]=mp2[t2]=1;
                        }
                    }
    	printf("%d",ans);
    	fclose(stdin);fclose(stdout);
        return 0;
    }
    

    正解及代码

    T1

    选择任意一个集合,得到的收益和都可以表示为一个一次函数的形式。

    我们只关心这些一次函数的最大值,可以发现这个最大值一定是先降后增、单调递增或者单调递减。

    图片.png

    因此我们只需要(check)一下(0)时刻是否符合条件,如果不符合则进行二分。 注意(check)的时候我们只需要找出最大的(m)个即可,因此可以(O(n))地做,具体做法是快排的过程中只递归一边,或者直接用(STL)(nth_element())即可

    #include <bits/stdc++.h>
    
    #define For(i, j, k) for (int i = j; i <= k; i++)
    
    using namespace std;
    
    const int N = 1e6 + 10;
    
    typedef long long LL;
    
    int n, m;
    LL S;
    
    int k[N], b[N];
    LL val[N];
    
    bool check(int x) {
        For(i, 1, n) val[i] = 1ll * k[i] * x + b[i];
        nth_element(val + 1, val + m, val + n + 1, greater<LL>());
        LL sum = 0;
        For(i, 1, m) if (val[i] > 0 && (sum += val[i]) >= S) return true;
        return sum >= S;
    }
    
    int main() {
    
        scanf("%d%d%lld", &n, &m, &S);
        For(i, 1, n) scanf("%d%d", &k[i], &b[i]);
    
        if (check(0)) { puts("0"); return 0; }
    
        int L = 1, R = 1e9;
        while (L < R) {
            int mid = (L + R) / 2;
            if (check(mid)) R = mid;
            else L = mid + 1;
        }
        printf("%d
    ", L);
    
        return 0;
    }
    

    T2

    每个变量都可以表示成(x_i=k+x_1)或者(x_i=k−x_1)的形式,表示为这个形式之后就可以方便地回答询问了。

    对于询问(1),只需要将表示(u)(v)的式子加起来,这时会出现两种情况:要么会得到(x_u+x_v=t)的形式,此时只需要判断是否有(s=t);要么会得到(x_u+x_v=t+2x_1)(x_u+x_v=t−2x_1),此时可以解出(x_1),注意判断解是否是整数即可。

    对于修改操作,实际上是修改一个子树内的变量的(k),这里可以将深度为奇数和偶数的点分开考虑,不难发现就是区间加减。由于只需要单点询问,用一个树状数组维护即可

    (O((n+q)log n)​)

    #include <bits/stdc++.h>
    
    #define getchar getchar_unlocked
    #define For(i, j, k) for (int i = j; i <= k; i++)
    
    using namespace std;
    
    int Read() {
        char c = getchar(); int x = 0;
        int sig = 1;
        while (c < '0' || c > '9') { if (c == '-') sig = -1; c = getchar(); }
        while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * sig;
    }
    
    const int N = 1e6 + 10;
    
    int n, q;
    
    struct Binary_Indexed_Tree {
    
        int c[N];
    
        inline int lowbit(int x) {
            return x & (-x);
        }
    
        void add(int x, int w) {
            while (x <= n) {
                c[x] += w;
                x += lowbit(x);
            }
        }
    
        int sum(int x) {
            int ret = 0;
            while (x) {
                ret += c[x];
                x -= lowbit(x);
            }
            return ret;
        }
    
    }T;
    
    int dfn[N], rdfn[N], dep[N];
    int fa[N], w[N];
    vector<int> G[N];
    
    void DFS_init(int o) {
        static int clk = 0;
        dfn[o] = ++clk;
        for (int v : G[o]) dep[v] = dep[o] ^ 1, DFS_init(v);
        rdfn[o] = clk;
    }
    
    int main() {
    
        n = Read(), q = Read();
        For(i, 2, n) fa[i] = Read(), w[i] = Read(), G[fa[i]].push_back(i);
        DFS_init(1);
        For(i, 2, n) if (!dep[i]) w[i] = -w[i];
        For(i, 2, n) T.add(dfn[i], w[i]), T.add(rdfn[i] + 1, -w[i]);
    
        while (q--) {
            int op = Read();
            if (op == 1) {
                int u = Read(), v = Read(), s = Read();
                int x = T.sum(dfn[u]), y = T.sum(dfn[v]);
    
                if (dep[u] && dep[v]) {
                    long long rt = 1ll * x + y - s;
                    if (rt % 2) puts("none");
                    else printf("%lld
    ", rt / 2);
                } else if (!dep[u] && !dep[v]) {
                    long long rt = 1ll * x + y + s;
                    if (rt % 2) puts("none");
                    else printf("%lld
    ", rt / 2);
                } else {
                    if (dep[v]) swap(u, v), swap(x, y);
                    if (x - y == s) puts("inf");
                    else puts("none");
                }
    
            } else {
                int u = Read(), nw = Read();
                if (!dep[u]) nw = -nw;
                T.add(dfn[u], nw - w[u]), T.add(rdfn[u] + 1, w[u] - nw);
                w[u] = nw;
            }
        }
    
        return 0;
    }
    

    T3

    #include <bits/stdc++.h>
    
    #define For(i, j, k) for (int i = j; i <= k; i++)
    #define Forr(i, j, k) for (int i = j; i >= k; i--)
    
    using namespace std;
    
    const int N = 2510;
    const int Mod = 1e9 + 7;
    
    int n, m = 2500;
    
    struct Binary_Indexed_Tree {
    
        int c[N];
    
        void init() {
            For(i, 1, m) c[i] = 0;
        }
    
        int lowbit(int x) { return x & (-x); }
    
        void add(int x, int w) {
            for (; x <= m; x += lowbit(x)) c[x] += w;
        }
    
        int sum(int x) {
            int ret = 0;
            for (; x; x -= lowbit(x)) ret += c[x];
            return ret;
        }
    
    }T, S;
    
    int pos[N][N];
    int c[N];
    bool vis[N];
    
    void upd(int x) {
        if (vis[x]) return;
        vis[x] = true;
        T.add(x, 1), S.add(x, x);
    }
    
    int main() {
    
        scanf("%d", &n);
        For(i, 1, n) {
            int x, y;
            scanf("%d%d", &x, &y);
            pos[x][++c[x]] = y;
        }
    
        For(i, 1, m) sort(pos[i] + 1, pos[i] + c[i] + 1), pos[i][c[i] + 1] = m + 1;
    
        int ans = 0;
        For(i, 1, m) if (c[i]) {
            T.init(), S.init();
            For(j, 1, m) vis[j] = false;
            For(j, 1, c[i]) upd(pos[i][j]);
            Forr(j, i - 1, 1) if (c[j]) {
                int pa = 1, pb = 1, cur = max(pos[i][1], pos[j][1]);
                For(k, 1, c[j]) upd(pos[j][k]);
                while (pos[i][pa + 1] <= cur) ++pa;
                while (pos[j][pb + 1] <= cur) ++pb;
    
                while (pa <= c[i] && pb <= c[j]) {
                    int nxt = min(pos[i][pa + 1], pos[j][pb + 1]), L = min(pos[i][pa], pos[j][pb]);
                    ans = (ans + (1ll * (S.sum(nxt - 1) - S.sum(cur - 1)) * T.sum(L) 
                                - 1ll * (T.sum(nxt - 1) - T.sum(cur - 1)) * S.sum(L)) * (i - j)) % Mod;
                    cur = nxt;
                    if (pos[i][pa + 1] <= cur) ++pa;
                    if (pos[j][pb + 1] <= cur) ++pb;
                }
            }
        }
    
        printf("%d
    ", ans);
    
        return 0;
    }
    

    下午讲课:DP选讲

    例1

    一个长度为(n)的序列(A),我们称一个元素是好的,当且仅当它严格大于相邻的元素。你可以进行若干次操作,每次将一个元素减小(1)
    对于每个(kin [1,lceilfrac{n}{2} ceil])求至少要进行多少次操作使得序列中至少有(k)个好的元素
    (nle 5000,A_ile 10^5)

    解:
    稍作观察发现,如果最终方案中一个位置是好的,那我们一定不会对它做操作;如果不是,它最终的值是(A_{i-1}-1,A_i,A_{i+1}-1)中的一个

    (dp[i][j][0])表示前(i)个元素有(j)个是好的,且已经钦定(A_i)是好的,此
    时对前(i)个元素至少要进行的操作次数。转移到(i+1)时需要确保操作后(A_{i+1}lt A_i)
    类似地,(dp[i][j][1/2/3])表示(A_i)不是好的时的三种情况
    (O(n^2))

    例2

    一个长度为(n)的序列(A),定义一个1到n的排列p是合法的,当且仅当(forall iin[1,n-1].A_{p_i} imes A_{p_{i+1}})不是完全平方数
    求有多少合法的排列,对(1e9+7)取模
    (nle 300,A_ile 10^9)

    解:
    对于每个元素去掉它的平方质因子,问题转化为有多少排列(p)满足(forall iin[1,n-1],A_{p_i} e A_{p_{i+1}}),即相邻元素不同
    先统计有多少种不同的元素,以及每种元素的个数。考虑每次将值相同的所有元素加入排列后的序列
    (dp[i][j])表示已经将前(i)种元素加入序列,有(j)对相邻位置相同的方案数。转移时枚举将第(i+1)种元素加入后会将多少对原来不合法的相邻位置拆开,以及会新增多少不合法的相邻位置即可。
    总元素个数(O(n)),因此复杂度最坏(O(n^3))

    例3

    有一个长度为(n)的序列(A)和常数(L,P),你需要将它分成若干段,每一段的代价为(|(sum A_i)-L|^P),求最小代价的划分方案

    (nle 10^5,1le Ple 10)

    解:

    [dp[j]=min_{i=0}^{j-1}|sum_j-sum_i-L|^P+dp[i] ]

    这个方程具有决策单调性,即(forall ult vlt ilt j),若在(i)处决策(v)优于决策(u),则在(j)处必有(v)优于决策(u)

    用一个栈维护每个决策更新的区间,新加入一个决策时可以二分得到它的区间

    (O(nlog n))

    证明:

    本题的证明需要讨论绝对值符号,先考虑里面的值为正的情况,其余的情况类似。

    定义(f_i(x)=dp[i]+(sum_x-sum_i-L)^P),只需证明(g(x)=f_u(x)-f_v(x),ult vlt x)单调增

    也就是随着(x)增大决策(u)相较于决策(v)越来越不优

    [g(x)=(sum_x-sum_u)^P-(sum_x-sum_v)^P+dp[u]-dp[v]\ g'(x)=P(sum_x-sum_u)^{P-1}-P(sum_x-sum_v)^{P-1}\ g'(x)ge 0 ]

    例4

    一张(n)个点(m)条边的带权有向图,每条边的长度都为(1)。求一条最长的路径,满足边权严格递增,且路径上边的顺序与输入中这些边的相对顺序相同。

    (n,mle 10^5)

    解:

    按输入顺序考虑每一条边。设(dp[i][j])表示路径到了节点(i),上一条边的权值(j)时的最长长度。

    显然有用的状态是(O(m))的。对每个点维护一个(set)来存这些状态以及它们的(dp)值,并保证(dp)值是随(j)递增的。这样加入一条边((u,v))时,只需要在节点(u)(set)上二分就可以进行转移了。同时还需要维护(v)(set)
    (O(n+mlog m))

    例5

    有一棵(n)个点的带权二叉树(不知道树的形态),给出对这棵二叉树进行中序遍历得到的权值序列,判断是否存在与之相符的一棵二叉树,树上每对相邻节点权值的(gcd)大于(1)

    (nle 700,w_ile 10^9)

    对于二叉树的一棵子树,其中序遍历是一段连续的区间。一个想法(跟昨天的题目很像)是设(dp[l][r][i])表示是否能将区间([l,r])建成一棵以(i)为根的合法二叉树,转移枚举两边子树的根,但这样的复杂度是(O(n^5))

    注意到对于区间([l,r])构成的二叉树,除非(l=1,r=n),否则它一定是(r+1)的左子树,或者(l-1)的右子树。因此我们只关心根节点与(l-1)(r+1)的权值的(gcd)是否为(1),而不需要知道根是哪个节点。

    于是状态数变为(O(n^2)),转移仍然枚举根即可,(O(n^3))

  • 相关阅读:
    java并发编程:线程安全管理类--原子操作类--AtomicReferenceArray<E>
    java并发编程:线程安全管理类--原子操作类--AtomicReference<V>
    java并发编程:线程安全管理类--原子操作类--AtomicMarkableReference<V>
    java并发编程:线程安全管理类--原子操作类--AtomicLongFieldUpdater<T>
    java并发编程:线程安全管理类--原子操作类--AtomicLongArray
    RabbitMQ安装
    git push error: failed to push some refs to怎么解决
    GitLab的安装和启动、访问
    go mod模式下如何使用本地项目中的包
    推荐,慕课网好课,仿阿里系优酷网-企业级Go改造PHP项目踩坑避坑指北
  • 原文地址:https://www.cnblogs.com/kuaileyongheng/p/9774570.html
Copyright © 2020-2023  润新知