这次考试,发挥不是很好,首先看了看三道题,1,2,3按顺序开题,看T1题面,首先我理解了半天,才明白这个图是左上角是(1,1),我刚开始一直把左下角当成(1,1)来处理,跟样例对不上,然后我就想到了差分,但是我不会利用差分数组求值,就放弃了这个想法,(结果正解就是差分。。。)我先打了一个暴力,又特判了一个特殊性质,但是判错了。
然后是T2,这题我当时不太明白,打暴搜打假了,如果有两种选择,那么我们要选最优的决策再*2,这道题利用状压的思想再加上记忆化搜索就可以过。T3,这道题我在考场上连暴力也没打出来,有时间的原因,还有思路的原因,这道题也是一个记忆话搜索,下面我进行题目讲解:

T1 u

思路,二维差分数组,考虑维护一个竖着的差分数组s1,和一个斜着的差分数组 s2,每次修改,我们都只需要修改差分数组两边的值,举个例子,假设现在给出的四个数 r,c,l,s, 那么我们只需要让 s1[r][c]+=m,s1[r+l][c]-=m,
s2[r][c+1]-=m,s2[r+l][c+l+1]+=m,其实自己手模一下就可以理解
代码如下:

#include<bits/stdc++.h>
#define int long long
#define re register int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
#define iv inline void
#define ii inline int
#define D double
using namespace std;
const int N=1e4+10;
int n,q,r,c,l,s;
int s1[N][N],s2[N][N];
ii read()
{
   int x=0,f=1;
   char ch=getchar();
   while(ch<'0'||ch>'9')
   {
   	if(ch=='-')
   		f=0;
   	ch=getchar();
   }
   while(ch>='0'&&ch<='9')
   {
   	x=(x<<1)+(x<<3)+(ch^48);
   	ch=getchar();
   }
   return (f)?x:(-x);
}
signed main()
{
   n=read();
   q=read();
   for(re i=1;i<=q;i++)
   {
   	r=read();
   	c=read();
   	l=read();
   	s=read();
   	s1[r][c]+=s;
   	if(c+1<=n)
   		s2[r][c+1]-=s;
   	if(r+l<=n)
   	{
   		s1[r+l][c]-=s;
   		if(c+l+1<=n)
   			s2[r+l][c+l+1]+=s;
   	}
   }
   long long out=0; 
   for(re i=1;i<=n;i++)
   {
   	long long ans=0;
   	for(re j=1;j<=n;j++)
   	{
   		s1[i][j]+=s1[i-1][j];
   		s2[i][j]+=s2[i-1][j-1];
   		ans+=s1[i][j]+s2[i][j];
   		out^=ans;
   	}
   }
   printf("%lld\n",out);
}

T2 v

思路:其实理解了题面后,这道题就是一个非常暴力的记忆化搜索,利用状压的思想暴力枚举每个状态最后取max,就是最优策略最大的期望值,这里面的序列更新操作比较妙,最后注意小的范围利用数组,大的范围利用map存储就可以过了
代码如下;



#include<bits/stdc++.h>
#define int long long
#define re register int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
#define iv inline void
#define ii inline int
#define D double
using namespace std;
map<int,double>f[31];
double dp[24][1<<23];
int n,k;
char s[50];
ii read()
{
   int x=0,f=1;
   char ch=getchar();
   while(ch<'0'||ch>'9')
   {
   	if(ch=='-')
   		f=0;
   	ch=getchar();
   }
   while(ch>='0'&&ch<='9')
   {
   	x=(x<<1)+(x<<3)+(ch^48);
   	ch=getchar();
   }
   return (f)?x:(-x);
}
inline double dfs(int sta,int p)
{
   if(p==n-k)
   	return 0;
   if(p<=23&&dp[p][sta]!=-1.00)
   	return dp[p][sta];
   if(p>23&&f[p].find(sta)!=f[p].end())
   	return f[p][sta];
   int l=(p>>1);
   int co,co1,co2,to1,to2;
   double sum=0.00000000;
   for(re i=1;i<=l;i++)
   {
   	co1=sta>>i-1&1;
   	co2=sta>>p-i&1;
   	to1=sta>>1&~((1<<i-1)-1)|sta&((1<<i-1)-1);
   	to2=sta>>1&~((1<<p-i)-1)|sta&((1<<p-i)-1);
   	sum+=2*max(dfs(to1,p-1)+co1,dfs(to2,p-1)+co2);
   }
   if(p&1)
   {
   	co=(p+1)>>1;
   	co1=sta>>(co-1)&1;
   	to1=sta>>1&~((1<<co-1)-1)|sta&((1<<co-1)-1);
   	sum+=dfs(to1,p-1)+co1;
   }
   return p>23?f[p][sta]=sum/(double)p:dp[p][sta]=sum/(double)p;
}
signed main()
{
   n=read();
   k=read();
   scanf("%s",s);
   int sta=0;
   for(re i=n-k+1;i<=min(n,23ll);i++)
   {
   	for(re j=0;j<=(1<<i);j++)
   		dp[i][j]=-1.00;
   }
   for(re i=1;i<=n;i++)
   {
   	if(s[i-1]=='W')
   		sta|=(1<<(n-i));
   }
   printf("%.10lf\n",dfs(sta,n));
}

T3 w

思路:考虑最后翻转的边集 S,最小操作数为 {V, S} 中奇数度数的点的一半,最小操作总长为 |S|。树形 dp 即可,dp[i][0/1] 记录以 i 为根的子树内,i 与父亲之间的边是否翻转,最少的奇度数点数、此时最小总长度;
那么 设 w1为儿子翻转总数为奇数的情况,w2为儿子翻转总数为偶数的情况
dp[i][1]=min((w1.c1,w1.c2+1),(w2.c1+1,w2.c2+1)).
dp[i][0]=min((w1+1,c2),(w2.c1,w2.c2))
代码如下:



#include<bits/stdc++.h>
//#define int long long
#define re register int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
#define iv inline void
#define ii inline int
#define D double
using namespace std;
const int N=1e5+10;
const int INF=1e9;
int n,a,b,c,d,tot;
int to[N<<1],next[N<<1],head[N<<1],col[N<<1];
struct Node
{
	int c1,c2;
	Node friend operator + (Node a,Node b)
	{
		return (Node){a.c1+b.c1,a.c2+b.c2};
	}
	bool friend operator < (Node a,Node b)
	{
		return a.c1==b.c1?a.c2<b.c2:a.c1<b.c1;
	}
}f[N][2];
Node min(Node x,Node y)
{
	if(x<y)	return x;
	return y;
}
ii read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return (f)?x:(-x);
}
iv add(int x,int y,int z)
{
	to[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
	col[tot]=z;
}
iv dfs(int st,int fa,int tp)
{
	Node w1=(Node){INF,INF},w2=(Node){0,0},u1,u2;
	for(re i=head[st];i;i=next[i])
	{
		int p=to[i];
		if(p==fa)
			continue;
		dfs(p,st,col[i]);
	//	u1=w2+f[p][1];
		u1=min(w2+f[p][1],w1+f[p][0]);
		u2=min(f[p][1]+w1,f[p][0]+w2);
		w1=u1;
		w2=u2;
	}
	if(tp==1||tp==2)
		f[st][1]=min((Node){w1.c1,w1.c2+1},(Node){w2.c1+1,w2.c2+1});
	else
		f[st][1]=(Node){INF,INF};
	if(tp==0||tp==2)
		f[st][0]=min((Node){w1.c1+1,w1.c2},(Node){w2.c1,w2.c2});
	else
		f[st][0]=(Node){INF,INF};
}
signed main()
{
	n=read();
	for(re i=1;i<n;i++)
	{
		a=read();
		b=read();
		c=read();
		d=read();
		if(d==2)
			c=2;
		else
			c=(c!=d)?1:0;
		add(a,b,c);
		add(b,a,c);
	}
	dfs(1,0,2);
	printf("%d %d",f[1][0].c1/2,f[1][0].c2);
}