这次考试,发挥不是很好,首先看了看三道题,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);
}