zoukankan      html  css  js  c++  java
  • 雅礼学习10.3

    雅礼学习10.3

    各题状况

    T1

    暴力修改+一维差分+二维差分

    莫名其妙就没了49分。。。

    好像是数组开的不够大?

    T2

    这。。。概率和期望,一会不会,连那个一分的部分分都没有任何思路

    T3

    题目并没有看太懂。。

    写了一个枚举算法,然后对某个一分的数据输出显然的结果

    。。。

    然后就只拿了1分

    枚举挂了,因为会错了题目含义

    题目及考场代码

    T1

    图片.png

    图片.png

    /*
     * 一个个修改肯定超时。。
     * q==0的直接输出0
     * 19分应该是暴力
     *
     * 考虑对每次操作,计算一共修改了多少个位置
     * 奇数个的话就让当前答案异或这个数字
     * 对于边界单独讨论
     * 但是问题在于。。。如果修改的位置刚好把当前答案所在位置变大了,就不应该是异或,而是+
     * 真的不会维护啊
     * 假装这么写是正确的吧。。。
     *
     * 每行差分
     * 最后O(n^2)统计
     */
    #include <cstdio>
    
    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;}
    
    const int N=1002;
    long long map[N][N],add[N][N],cut[N][N];
    
    int main()
    {
    	freopen("u.in","r",stdin);
    	freopen("u.out","w",stdout);
    	int n=read(),q=read(),r,c,s,l,x;
    	long long ans=0;
    	if(q==0)
    	{
    		printf("0");
    		goto E;
    	}
    	if(q<=400)
    	{
    		while(q--)
    		{
    			r=read(),c=read(),l=read(),s=read();
    			x=min(r+l-1,n);
    			for(int i=r;i<=x;++i)
    				for(int j=c;j<=min(i-r+c,n);++j)
    					map[i][j]+=s;
    		}
    		for(int i=1;i<=n;++i)
    			for(int j=1;j<=n;++j)
    				ans^=map[i][j];
    	}
    	else
    		if(q<=2000)
    		{
    			while(q--)
    			{
    				r=read(),c=read(),l=read(),s=read();
    				x=min(r+l-1,n);
    				for(int i=r;i<=x;++i)
    					map[i][c]+=s,map[i][min(i-r+c,n)+1]+=-s;
    			}
    			for(int i=1;i<=n;++i)
    			{
    				x=0;
    				for(int j=1;j<=n;++j)
    				{
    					x+=map[i][j];
    					printf("%d ",x);
    					ans^=x;
    				}
    				puts("");
    			}
    		}
    		else
    		{
    			while(q--)
    			{
    				r=read(),c=read(),l=read(),s=read();
    				add[r][c]+=s,add[r+l][c]-=s;
    				cut[r][c+1]+=s,cut[r+l][c+l+1]-=s;
    			}
    			for(int i=1;i<=n;++i)
    				for(int j=1;j<=n;++j)
    				{
    					add[i][j]+=add[i-1][j];
    					cut[i][j]+=cut[i-1][j-1];
    				}
    			for(int i=1;i<=n;++i)
    				for(int j=1;j<=n;++j)
    				{
    					map[i][j]+=map[i][j-1]+add[i][j]-cut[i][j];
    					ans^=map[i][j];
    				}
    		}
    			
    	/*
    	else
    	{
    		while(q--)
    		{
    			r=read(),c=read(),l=read(),s=read();
    			x=l+1;
    			if(r+l-1>n || c+l-1>n)
    			{//想了半天没想出来怎么快速把多余的部分删掉。
    				x=0;
    				for(int i=r;i<=min(r+l-1,n);++i)
    					x+=min(i-r+c,n);
    			}
    			if(x&1)ans^=s;
    		}
    	}*/
    	printf("%lld",ans);
    			
    E:	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

    T2

    图片.png

    图片.png

    /*
     * 概率。。。
     * 根本一会不会啊。。。
     * 骗分走人
     */
    #include <cstring>
    #include <cstdio>
    
    const int N=31;
    char s[N];
    
    int main()
    {
    	freopen("v.in","r",stdin);
    	freopen("v.out","w",stdout);
    
    	scanf("%d%d",&n,&k);
    	scanf("%s",s);
    	int x=0;
    	for(int i=0;i<n;++i)
    		if(s[i]=='W')
    			++x;
    	if(k==0 || k==n)
    		printf("%.10lf",(double)1.0*k);
    	else	printf("%.10lf",(double)x*1.0/2);
    
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

    T3

    图片.png

    图片.png

    /*
     * 输出0有1分。
     */
    #include <cstdio>
    #include <vector>
    
    int n,step,cnt,k_2,z_2,ku;
    int l_note[100023];
    struct note{
    	int con,have,wash;
    };
    bool dis[100023];
    std::vector<note> tree[100023];
    std::vector<int> kuai[100023];
    void search(int num)
    {
    	for(int space,i=0;i<l_note[num];i++)
    	{
    		space=tree[num][i].con;
    		if(((tree[num][i].wash!=tree[num][i].have)||(tree[num][i].wash==2))&&dis[space]!=true)
    		{
    			tree[num][i].have=tree[num][i].wash;
    			++cnt;
    			dis[num]=true;
    			search(space);
    			break;
    		}
    	}
    }
    bool vis[100023];
    void qk(int num)
    {
    	vis[num]=true;
    	for (int i=0;i<kuai[num].size();++i)
    	{
    		int space=kuai[num][i];
    		if(tree[num][i].wash==2)++k_2;
    		if(vis[space]!=true)
    			qk(space);
    	}
    }
    int main ()
    {
    	freopen("w.in","r",stdin);
    	freopen("w.out","w",stdout);
    	scanf("%d",&n);
    	if(n<=1000)
    	{
    		for(int a,b,c,d,i=1;i<n;++i)
    		{
    			scanf("%d%d%d%d",&a,&b,&c,&d);
    			tree[a].push_back((note){b,c,d});
    			++l_note[a];
    			tree[b].push_back((note){a,c,d});
    			++l_note[b];
    			if((c!=d)||(d==2))
    			{
    				kuai[a].push_back(b);
    				kuai[b].push_back(a);
    			}
    			if(d==2)++z_2;
    		}
    		for(int i=1;i<=n;++i)
    		{
    			for(int o=0;o<l_note[i];o++)
    			{
    				if((tree[i][o].wash!=tree[i][o].have)&&tree[i][o].wash!=2)
    				{
    					dis[tree[i][o].con]=true;
    					search(i);
    					break;
    				}
    			}
    		}
    		for(int i=1;i<=n;++i)
    			if(vis[i]!=true&&kuai[i].size()!=0)
    			{
    				++ku;
    				qk(i);
    			}
    		step=ku-(z_2-k_2/2);
    		printf("%d %d",step,cnt);
    	}
    	else	printf("0 0");
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

    正解

    T1

    第一眼(n imes q)做法,(3e8)显然不能过

    那么考虑对其进行优化:差分序列的访问时不连续的,所以我们考虑把它变成连续的

    就过了。。。

    官方正解:

    竖着做一次差分,斜着做一次差分,那么一个三角形就可以确定出来了,而后(n^2)扫一遍求异或和

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int maxn=1e3+10;
    int n,q;
    ll a[maxn][maxn],b[maxn][maxn],ans;
    
    inline void add_a(int x,int y,int v){
    	if(x<=n&&y<=n)
    		a[x][y]+=v;
    }
    inline void add_b(int x,int y,int v){
    	if(x<=n&&y<=n)
    		b[x][y]+=v;
    }
    
    int main(){
    	freopen("u.in","r",stdin);
    	freopen("u.out","w",stdout);
    	scanf("%d%d",&n,&q);
    	while(q--){
    		int r,c,l,s;
    		scanf("%d%d%d%d",&r,&c,&l,&s);
    		add_a(r,c,s);
    		add_a(r+l,c+l,-s);
    		add_b(r+l,c,-s);
    		add_b(r+l,c+l,s);
    	}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j){
    			if(i>1)
    				a[i][j]+=a[i-1][j-1]+a[i-1][j]-a[i-2][j-1];
    			else
    				a[i][j]+=a[i-1][j-1]+a[i-1][j];
    			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
    			ans^=a[i][j]+b[i][j];
    		}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    T2

    可以得到一个(O(2^n imes n))的状压(DP)的做法,记录每个球是否还没有被移除,然后按照最优策略期望移除白球数

    事实上有很多重复状态,也就是剩下的求的颜色序列相同时结果是一样的

    考虑将状态记成剩下的颜色序列,长度较小的时候就直接用数组去存,较大的时候用(map)去存

    状态个数能得到一个上界是(sum_{i=0}^nmin{2^i,{ichoose n}})(事实上最大值为(sum_{i=1}^{n+1}Fib_i)但是这一点也不(noip)

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=30+5;
    int n,k;
    char s[maxn];
    
    namespace ${
    	const int xxx=24;
    	double a[1<<xxx+1];
    	map<int,double> m[maxn];
    	inline void init(){
    		for(int i=0;i<1<<xxx+1;++i)
    			a[i]=-1;
    	}
    	inline bool count(int bit,int len){
    		if(len<=xxx)
    			return a[1<<len|bit]!=-1;
    		else
    			return m[len].count(bit);
    	}
    	inline double&find(int bit,int len){
    		if(len<=xxx)
    			return a[1<<len|bit];
    		else
    			return m[len][bit];
    	}
    }
    inline int erase(int bit,int k){
    	return bit&(1<<k)-1|bit>>1&-1<<k;
    }
    inline double max_(double a,double b){
    	return a>=b?a:b;
    }
    double dfs(int bit,int len){
    	if(len<=k)
    		return 0;
    	if($::count(bit,len))
    		return $::find(bit,len);
    	double&res=$::find(bit,len);
    	res=0;
    	for(int i=0,j=len-1;i<=j;++i,--j)
    		if(i<j)
    			res+=max_(dfs(erase(bit,i),len-1)+(bit>>i&1),dfs(erase(bit,j),len-1)+(bit>>j&1))*2;
    		else
    			res+=dfs(erase(bit,i),len-1)+(bit>>i&1);
    	return res/=len;
    }
    
    int main(){
    	freopen("v.in","r",stdin);
    	freopen("v.out","w",stdout);
    	$::init();
    	scanf("%d%d%s",&n,&k,s);
    	k=n-k;
    	int bit=0;
    	for(int i=0;i<n;++i)
    		bit|=(s[i]=='W')<<i;
    	printf("%.10f
    ",dfs(bit,n));
    	return 0;
    }
    

    T3

    如果最后翻转的边集是(S),最少操作数为({V,S})中奇数度数的点的一半,最小操作总长度是(|S|)

    考虑树形(DP)(dp[i][0/1])记录以(i)为根的子树内,(i)与父亲之间的边是否翻转,最少的奇数度数的点的个数,此时的最小总长度

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=1e5+10;
    const pair<int,int> inf=make_pair(1e9,1e9);
    int n;
    vector<pair<int,int> > g[maxn];
    pair<int,int> dp[maxn][2];
    
    inline pair<int,int> operator+ (pair<int,int> a,pair<int,int> b){
    	return make_pair(a.first+b.first,a.second+b.second);
    }
    void dfs(int pos,int fa,int type){
    	pair<int,int> tmp0(0,0),tmp1(inf);
    	for(int i=0,v;i<g[pos].size();++i)
    		if((v=g[pos][i].first)!=fa){
    			dfs(v,pos,g[pos][i].second);
    			pair<int,int> nxt0,nxt1;
    			nxt0=min(tmp0+dp[v][0],tmp1+dp[v][1]);
    			nxt1=min(tmp1+dp[v][0],tmp0+dp[v][1]);
    			tmp0=nxt0;tmp1=nxt1;
    		}
    	if(type==0||type==2)
    		dp[pos][0]=min(tmp0,make_pair(tmp1.first+1,tmp1.second));
    	else
    		dp[pos][0]=inf;
    	if(type==1||type==2)
    		dp[pos][1]=min(make_pair(tmp0.first+1,tmp0.second+1),make_pair(tmp1.first,tmp1.second+1));
    	else
    		dp[pos][1]=inf;
    }
    
    int main(){
    	freopen("w.in","r",stdin);
    	freopen("w.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1,a,b,c,d;i<n;++i){
    		scanf("%d%d%d%d",&a,&b,&c,&d);
    		if(d!=2)
    			d=(c!=d);
    		g[a].push_back(make_pair(b,d));
    		g[b].push_back(make_pair(a,d));
    	}
    	dfs(1,0,0);
    	printf("%d %d
    ",dp[1][0].first/2,dp[1][0].second);
    	return 0;
    }
    

    下午讲课:搜索与剪枝

    主要是题目只有题目

    例一

    有一个([1,2^n])的排列(A{1,cdots,2^n})

    可以执行的操作有(n)种,每种操作最多可以执行一次。第(i)种操作:将序列从左到右划分为(2^{n-i+1})段,每段恰好包括(2^{i-1})个数,然后整体交换其中两段。
    求可以将数组(A)从小到大排序的不同的操作序列有多少个。
    两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同)。

    (nle 12)

    解:

    首先,任意一个合法的操作序列,我们可以改变其顺序,依然满足条件。
    那么这一类的操作序列的贡献,即为操作次数的阶乘。
    那么,现在只考虑种类编号递增的操作序列。第(i)种操作时,序列分成了大小为(2^{i-1})的段,如果某个段不是递增且连续的,那么最后肯定不会满足条件。所以,在这种操作考虑完后,每个大小为(2^i)的段应当递增且连续。
    当考虑第(i)种操作时:

    • 如果不合条件的(2^i)大小的段超过(2)个,直接退出
    • 如果不存在的话,直接继续
    • 如果只有(1)个,交换其包含的(2^{i-1})的两段,判断是否可行
    • 如果有(2)个就分别搜索(4)种交换方案

    例二

    农夫约翰需要一些特定规格的木材(共(n)块,长度不一定相同),可是他只剩下一些大规格的木板(共(m)块,长度不一定相同)。不过约翰可以将这些木板切割成他所需要的规格。
    求约翰最多能够得到多少他所需要的木材。

    (nle 1000,mle 50,lenthle 32767)

    解:

    显然可以二分答案。
    判断答案为(k)是否可行,显然可以将木材排序,搜索最短的k 块木材是否
    能得到。
    然后剪枝:

    • 从小到大搜索所需的木材从哪块木板得到,因为长的木材限制比较大
    • 如果两块所需木材长度相同,后搜索的那一块的来源只需要从前者的来源开始枚举
    • 当一块原料的长度比最短的需求还短,那么直接丢弃,如果丢弃总量+所需总量(gt)原料总量,剪掉

    例三

    给出一个数字(S),输出所有约数和等于(S)的数

    一共(T)次询问

    (Sle 2e9,Tle 100)

    解:

    (n=prod_i p_i^{a^i}Rightarrow sigma(n)=prod_isum_{j=0}^{a_i}p_i^j)

    那么我们可以通过枚举(p_i)及其(a_i)来搜索

    若当前需要得到的(S)可以表示为为一个未搜索过的质数与(1)的和,那么之前的数与这个质数的乘积是一个合法答案

    对于每个使得((p+1)(p-1)lt S)(p),枚举可能的(a_i)进行递归

    例四

    一个(n imes m)的网格,(k)种颜色,部分格子已经涂了某种颜色,现在需要将其他格子也涂上颜色,使得从((1,1))((n,m))的每条路径(每次向下或向右走一格)都不会出现重复颜色。求方案数,对(10^9+7)取模。

    (n,mle 1000,kle 10)

    解:

    颜色数应大于等于步数,(n+m-1le kRightarrow n+mle k+1le 11),否则puts("0")。
    然后搜每个位置的颜色,可以状压到每个位置的已经过的颜色。
    可行性剪枝:未经过的颜色数小于剩余步数,剪掉。
    对称性剪枝:如果涂上一个未出现过的颜色,涂哪一个都是等价的,那么只需搜其中一个。

    例五

    (52)张牌排成一排({0,1cdots 51}),然后可以把他们分成两半,然后交叉着洗牌(一个确定的置换)。
    洗完牌后,可能会出现一个错误,将一对相邻的牌调换了位置,但是每次洗牌最多只会犯一个错误。
    现在给出牌最终的顺序,求最少洗了多少次(保证不超过(10)),最少犯几次错误。

    解:

    一个性质:确定了洗牌次数后,若真实的最终排列与不犯错误的最终排列,有(k)个位置不同,那么犯错次数(lceilfrac{k}{2} ceil)

    以此来当做剪枝,枚举洗牌次数和犯错次数,跑(idA^*)

    例六

    你记录了([0,59])这个时间段内到站的所有公交车(数量(le 300)) 的时间,每辆车属于一条线路。

    • 同一路线路的车的到站时间间隔相同。
    • 每条线路在([0,59])至少到达两辆车。
    • 最多有(17)条线路

    求最少可能有多少条线路

    解:

    一条线路,可以通过第一辆、第二辆车来确定。
    我们可以按照到站顺序来确定每辆车的归属情况。
    如果它是线路第一辆车,那么先把他做一个标记。(同时,它的时间应在([0,29]))
    如果是第二辆,枚举标记过的车辆来确定一条线路,如果可行,删除该线
    路的所有车。
    这样,随着搜索的深度增加,可选车辆快速减少,搜索规模大量降低。

    同时可以加入若干合法性或者最优性剪枝。比如:作为第一辆的车多于未确定的车。

    例七

    给定(n)个字符集为({a,b,c,cdots,m})的字符串(S_i),求一种({a,b,c,cdots,m})({0,1,2,3,4,5,6,7,8,9,+, imes,=})的映射(f),使得所有(f(S_i))均是表达式合法、且成立的等式。

    (nle 1000,5le |S_i|le 11)

    解:

    先对约束条件较高的(+, imes ,=)进行搜索。
    然后,对于每一个等式,我们得到了每个数的位数,计算等号两侧的值域,
    无交集就可以剪枝。随着对应关系的确定,值域也会越来越小。
    可以选择从低位开始搜,确定了低位的值,不相等可以剪枝

  • 相关阅读:
    简单的模板解析函数
    HTML通过事件传递参数到js 二 event
    HTML通过事件传递参数到js一
    通过this获取当前点击选项相关数据
    LeetCode 20. 有效的括号(Valid Parentheses)
    LeetCode 459. 重复的子字符串(Repeated Substring Pattern)
    LeetCode 14. 最长公共前缀(Longest Common Prefix)
    LeetCode 168. Excel表列名称(Excel Sheet Column Title)
    LeetCode 171. Excel表列序号(Excel Sheet Column Number) 22
    LeetCode 665. 非递减数列(Non-decreasing Array)
  • 原文地址:https://www.cnblogs.com/kuaileyongheng/p/9743527.html
Copyright © 2011-2022 走看看