noip模拟19[u·v·w](雅礼集训)

noip模拟19 solutions

我我我我我我只有49pts,我要气死了

状态也不算太好,毕竟当时是改着改着上一场的最后一题就开始考试了

而且当是我极其愤怒,根本改不过

·

T1 u

就这个题,我在考场上又是手动给我的程序加了个log,然后别人都那61pts,只有我自己46pts

但是这个题正解确实在我的意识范围内,就是一个小小的差分,但是这个差分非常的叼钻

首先你得知道,还有一种斜着的差分,用式子表示就是\(f[i][j]=f[i-1][j-1]+f[i-1][j]-f[i-2][j-1]\)

画个图理解一下:

你在左上角加上一个1,我们利用差分的思想的话,直接就吧这个紫色的区域都加上了2

那要是下面加多了怎么办,在你要加的那个三角形的右下角的地方-1,

但是你发现好像还是多了,在绿色的地方加上一,黄色的地方减去一,我们要处理的是粉色的区域

那么你会发现,好像还有一块紫色多出来,那我们就直接利用正常的二维差分来解决

我们在黄色的地方再减1,右下角再加一,注意这次就用正常的差分就好了

\(f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]\)

这样的话,这个题就直接解决了,然后就维护两个差分数组就好了,

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e3+5;
const int Q=3e5+5;
int n,q;
ll jz[N][N],ans,sum[N][N];
void adda(int x,int y,int v){
	if(x<=n&&y<=n)jz[x][y]+=v;
}
void addb(int x,int y,int v){
	if(x<=n&&y<=n)sum[x][y]+=v;
}
signed main(){
	scanf("%d%d",&n,&q);
	for(re i=1,r,l,c,s;i<=q;i++){
		scanf("%d%d%d%d",&r,&c,&l,&s);
		adda(r+l,c,-s);
		adda(r+l,c+l,s);
		addb(r,c,s);
		addb(r+l,c+l,-s);
	}
	for(re i=1;i<=n;i++){
		for(re j=1;j<=n;j++){
			jz[i][j]+=jz[i-1][j]+jz[i][j-1]-jz[i-1][j-1];
			sum[i][j]+=sum[i-1][j-1]+sum[i-1][j];
			if(i>1)sum[i][j]-=sum[i-2][j-1];
			ans^=(jz[i][j]+sum[i][j]);
		}	
	}
	printf("%lld",ans);
}

·

T2 v

状压!!!

对一个裸的状压,然后用map维护,直接记忆化搜索,一点技术含量都没有

就是要注意那个倒位置的时候仔细点,卡常过,特判过,我特判过的

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define pa pair<int,int>
const int N=32;
int n,k;
char a[N];
int bas;
map<int,double> mp[15];
double q[24][1<<21];
double dfs(int s,int cz){
	if(cz>k)return 0.0;
	int len=n-cz+1;
	if(len<=21&&q[len][s]!=-1)return q[len][s];
	if(len>21&&mp[len-21].find(s)!=mp[len-21].end())return mp[len-21][s];
	double ans=0;
	for(re i=1;i<=len/2;i++){
		int tmp1=((s>>i)<<i-1)|(s&((1<<i-1)-1));
		int cho1=(s>>i-1)&1;
		int x=len-i+1;
		int tmp2=((s>>x)<<x-1)|(s&((1<<x-1)-1));
		int cho2=(s>>x-1)&1;
		//if(s==bas)cout<<i<<" "<<tmp1<<" "<<cho1<<"    "<<tmp2<<" "<<cho2<<endl;
		ans+=2.0*max(dfs(tmp1,cz+1)+cho1*1.0,dfs(tmp2,cz+1)+cho2*1.0)/(len*1.0);
	}
	if(len&1){
		int mid=len+1>>1;
		int tmp=((s>>mid)<<mid-1)|(s&((1<<(mid-1))-1));
		int cho=(s>>mid-1)&1;
		ans+=(dfs(tmp,cz+1)+cho*1.0)/(len*1.0);
	}
	if(len<=21)q[len][s]=ans;
	else mp[len-21][s]=ans;
	return ans;
}
signed main(){
	scanf("%d%d",&n,&k);
	scanf("%s",a+1);
	for(re i=1;i<=21;i++)
		for(re j=0;j<(1<<21);j++)
			q[i][j]=-1.0;
	int c=0,b=0;
	for(re i=1;i<=n;i++){
		if(a[i]=='W')
			bas|=(1<<i-1),c++;
		else b++;
	}
	if(c==b&&(k==n||k==n-1))printf("%.10lf",n/2.0);
	else printf("%.10lf",dfs(bas,1));
}

·

T3 w

这个题吧我本来是想dfs的但是后来发现我根本无法得到我要的状态,也根本无法得到转移方程

然后我就对着那个1分的点就上去了

考完后我看题解,有这么一个性质:

设我们要翻转的边集为S,那么我们的最少的路径条数就是这个边集所能连接的点中度数为奇数的点的一半,

一开始我还不信,后来就去手动模拟,然后试了好几组,都对了

其实是那些可以配对为偶数的边都可以连成一条,所以只有奇数点才为路径的端点而且需要除以2

这样我们就会有一个状态了,状态中有两个量,一个是路径条数,一个是边的数量

我们设f[i][1/0]表示i和他父亲的连边(1翻转,0不翻)为1/0时的最小路径条数,翻转边数

注意这里的状态包括和父亲的连边以及i的子树,转移就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
const int inf=0x3f3f3f3f;
int n;
int to[N*2],nxt[N*2],val[N*2],head[N],rp;
void add_edg(int a,int b,int c,int d){
	to[++rp]=b;
	if(c==d)val[rp]=0;
	else if(d==2)val[rp]=2;
	else val[rp]=1;
	nxt[rp]=head[a];
	head[a]=rp;
}
struct node{
	int sum,len;
	node(){}
	node(int x,int y){this->sum=x;this->len=y;}
	node operator + (node x)const {
		node ret;
		ret.sum=sum+x.sum;
		ret.len=len+x.len;
		return ret;
	}
	bool operator < (node x)const {
		if(sum!=x.sum)return sum<x.sum;
		return len<x.len;
	}
	void print(){
		printf("%d %d\n",sum,len);
	}
}dp[N][2];
void dfs(int x,int f,int typ){
	node t1(inf,inf),t2(0,0),n1,n2;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==f)continue;
		dfs(y,x,val[i]);
		n1=min(t1+dp[y][0],t2+dp[y][1]);
		n2=min(t1+dp[y][1],t2+dp[y][0]);
		t1=n1,t2=n2;
		//t1.print();t2.print();
	}
	if(typ==0){
		dp[x][0]=min(node(t1.sum+1,t1.len),t2);
		dp[x][1]=node(inf,inf);
	}
	else if(typ==1){
		dp[x][0]=node(inf,inf);
		dp[x][1]=min(node(t1.sum,t1.len+1),node(t2.sum+1,t2.len+1));
	}
	else {
		dp[x][0]=min(node(t1.sum+1,t1.len),t2);
		dp[x][1]=min(node(t1.sum,t1.len+1),node(t2.sum+1,t2.len+1));
	}
}
signed main(){
	/*node x(1,2),y(1,3);
	node z=min(x,y);
	cout<<z.sum<<" "<<z.len<<endl;*/
	scanf("%d",&n);
	for(re i=1,a,b,c,d;i<n;i++){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		add_edg(a,b,c,d);
		add_edg(b,a,c,d);
	}
	dfs(1,0,0);
	printf("%d %d",dp[1][0].sum/2,dp[1][0].len);
}
posted @ 2021-07-20 09:48  fengwu2005  阅读(51)  评论(0编辑  收藏  举报