题目描述
考虑一个 n ∗ n 的矩阵 A,初始所有元素均为 0。
执行 q 次如下形式的操作:给定 4 个整数 r, c, l, s,对于每个满足 x ∈ [r, r+l), y ∈ [c, x-r+c] 的元素 (x, y),将权值增加 s。也就是,给一个左上顶点为 (r, c)、直角边长为 l 的下三角区域加上 s。
输出最终矩阵的元素异或和。
输入格式
第一行两个整数 n, q。
接下来 q 行,每行四个整数 r, c, l, s,代表一次操作。
输出格式
输出一行,一个整数,代表答案。
样例
样例输入
10 4
1 1 10 1
5 5 4 4
1 9 4 3
3 3 5 2
样例输出
0
样例解释
1 0 0 0 0 0 0 0 3 0
1 1 0 0 0 0 0 0 3 3
1 1 3 0 0 0 0 0 3 3
1 1 3 3 0 0 0 0 3 3
1 1 3 3 7 0 0 0 0 0
1 1 3 3 7 7 0 0 0 0
1 1 3 3 7 7 7 0 0 0
1 1 1 1 5 5 5 5 0 0
1 1 1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1 1 1
数据范围与提示
对于100%的数据,保证 n ∈ [1, 103],q ∈ [0, 3 ∗ 105],r, c, l ∈ [1, n],s ∈ [1, 109]。
考虑用二维树组差分维护,如果是个矩形就是特别简单的了,可惜是个直角三角形
那么就拆一下,用两个二维数组,一个专门维护单列,一个专门维护斜边
#include
#define MAXN 2005
#define ll long long
int n, Q;
ll ans;
ll col[MAXN][MAXN], slope[MAXN][MAXN], matrix[MAXN][MAXN];
int main() {
scanf( "%d %d", &n, &Q );
for( int i = 1, r, c, l, s;i <= Q;i ++ ) {
scanf( "%d %d %d %d", &r, &c, &l, &s );
col[r][c] += s, col[r + l][c] -= s;
slope[r][c + 1] += s, slope[r + l][c + l + 1] -= s;
}
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= n;j ++ )
col[i][j] += col[i - 1][j];
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= n;j ++ )
slope[i][j] += slope[i - 1][j - 1];
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= n;j ++ )
matrix[i][j] += matrix[i][j - 1] + col[i][j] - slope[i][j];
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= n;j ++ )
ans ^= matrix[i][j];
printf( "%lld", ans );
return 0;
}
有 n 个球排成一行,每个球的颜色为黑或白。
执行 k 次操作,第 i(1 ≤ i ≤ k) 次操作形式如下:
• 从 [1, n-i+1] 中,等概率随机选择一个整数 x。
• 移除从左往右数的第 x 个球,或从右往左数的第 x 个球(也就是从左往右数的第 n-i+2-x 个)。之后,所有右侧的球的编号减 1。
给定每个球的颜色信息,希望最大化移除的白球数量。
输出在最优策略下,期望的移除白球数量。误差在1e-6范围内,即算正确
输入格式
第一行,两个整数 n, k。 第二行,一个长度为 n、仅由 ‘W’ 和 ‘B’ 组成的字符串,第 i 个字符代表第 i 个球的颜色, ‘W’ 为白色, ‘B’ 为黑色。
输出格式
输出一行,一个浮点数,代表答案。
样例
样例输入1
3 1
BWW
样例输出1
1.0000000000
样例解释1
如果 x = 1,从右侧操作,如果 x = 2 或 3,从左侧操作,均可以移除一个白球。
样例输入2
4 2
WBWB
样例输出2
1.5000000000
数据范围与提示
对于100%的数据,保证 1 ≤ n ≤ 30,0 ≤ k ≤ n
n n n怎么这么小?!!准定要搞事!!
老子反手就是一个状压,压不死你
但是仔细一想, 2 30 2^{30} 230好像逼近 i n t int int极限值
不要担心,一般这种大数据数组存不下,就找蓝翔技术学校 S T L STL STL,这些容器关键时刻还是不会掉链子的,这里可以采取 m a p map map与数组进行分工合作,数组存不下的就用 m a p map map去存
这道题仔细一看,就是个披着期望皮的记忆化搜索羊
为什么呢?很简单,你想嘛~
当后面剩下的颜色状态一样时,操作的概率与期望计算方式与之前肯定是一样的,答案也肯定一样
那我们为什么要傻逼兮兮的怼上去再来一遍??
接下来我将重点讲解代码中贼冗长的一行代码的意思,因为实在是太妙了!!
int sta1 = sta >> 1 & ~ ( ( 1 << i - 1 ) - 1 ) | sta & ( ( 1 << i - 1 ) - 1 );
如果不熟悉位运算的优先级的,看到这里基本上已经告退了吧哈哈哈哈
接下来让我们加几个括号
int sta1 = ( ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) ) | ( sta & ( ( 1 << ( i - 1 ) ) - 1 ) );
我们来一一分析,提前声明因为我的 i i i是从 1 1 1开始枚举的,而二进制是从 0 0 0开始,故要 − 1 -1 −1
如何才能把 i i i这一个球彻底删掉呢??让我们拭目以待吧!!
( 1 << ( i - 1 ) ) - 1
从 [ 0 , i ) [0,i) [0,i)位每一位都为 1 1 1然后其余位全为 0 0 0,好理解,下一个!
sta >> 1
~ ( ( 1 << ( i - 1 ) ) - 1 ) )
~ : 一种单目运算符(位运算),对二进制每一位上的数全都进行取反操作,1变0,0变1
接下来进行结合操作!!吃俺老孙一棒
( ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) )
这行语句的目的是保留了 [ i + 1 , t o t ] [i+1,tot] [i+1,tot]位上的每一个球的状态,即画圈部分
注意 s t a > > 1 sta>>1 sta>>1整体右移 1 1 1位后再取 & \& &,而我们知道除了 [ 0 , i ) [0,i) [0,i)其余位置都为 1 1 1
故而 s t a sta sta右移 1 1 1位后,以 i i i为分水岭,原来的状态是什么,右移 1 1 1位的状态亦不变
而 i i i恰好右移成为 i − 1 i-1 i−1,从 i − 1 i-1 i−1位开始往右全都是 0 0 0
刚好把 i i i给卡掉了!
最后就是上面一堆的状态 [ 0 , i ) [0,i) [0,i)还是均为 0 0 0的,此时就需要得到原来这些位置上的状态
sta & ( ( 1 << ( i - 1 ) ) - 1 )
最后两个残缺状态取 ∣ | ∣,即是卡掉 i i i后的新状态
这是其中一种,另一种同理,不再赘述
#include
#include
#include
using namespace std;
map < int, double > mp;
int n, k;
char s[40];
double dp[1 << 25];
double dfs( int sta, int tot ) {
if( tot == n - k ) return 0;
if( tot > 24 && mp.find( sta ) != mp.end() ) return mp[sta];
if( tot <= 24 && dp[sta] != -1 ) return dp[sta];
double sum = 0;
for( int i = 1;i <= ( tot >> 1 );i ++ ) {
int color1 = ( sta >> ( i - 1 ) ) & 1;
int color2 = ( sta >> ( tot - i ) ) & 1;
int sta1 = ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) | ( sta & ( ( 1 << ( i - 1 ) ) - 1 ) );
int sta2 = ( sta >> 1 ) & ( ~ ( ( 1 << ( tot - i ) ) - 1 ) ) | ( sta & ( ( 1 << ( tot - i ) ) - 1 ) );
sum += 2 * max( dfs( sta1, tot - 1 ) + color1, dfs( sta2, tot - 1 ) + color2 ) / tot;
}
if( tot & 1 ) {
int i = ( tot + 1 ) >> 1;
int color = ( sta >> ( i - 1 ) ) & 1;
int st = ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) | ( sta & ( ( 1 << ( i - 1 ) ) - 1 ) );
sum += ( dfs( st, tot - 1 ) + color ) / tot;
}
return ( tot > 24 ) ? mp[sta] = sum : dp[sta] = sum;
}
int main() {
scanf( "%d %d %s", &n, &k, s );
int sta = 0;
for( int i = 0;i < ( 1 << 25 );i ++ )
dp[i] = -1;
for( int i = 1;i <= n;i ++ )
sta |= ( s[i - 1] == 'W' ) << ( n - i );
sta |= ( 1 << n );
printf( "%.8f", dfs( sta, n ) );
return 0;
}
有一棵 n 个节点的树,每条边长度为 1,颜色为黑或白。
可以执行若干次如下操作:选择一条简单路径,反转路径上所有边的颜色。
对于某些边,要求在操作结束时为某一种颜色。
给定每条边的初始颜色,求最小操作数,以及满足操作数最小时,最小的操作路径长度和。
输入格式
第一行,一个正整数 n。
接下来 n-1 行,每行四个整数 a, b, c, d:
• 树中有一条边连接 a 和 b。
• c = 0, 1 表示初始颜色为白色、黑色。
• d = 0, 1, 2 表示最终要求为白色、要求为黑色、没有要求。
输出格式
输出一行,两个整数,表示最小操作数、操作数最小时的最小路径长度和。
样例
样例输入1
5
2 1 1 0
1 3 0 1
2 4 1 2
5 2 1 1
样例输出1
1 2
样例解释1
操作路径 {2, 1, 3}。
样例输入2
3
1 3 1 2
2 1 0 0
样例输出2
0 0
样例输入3
6
1 3 0 1
1 2 0 2
2 4 1 0
4 5 1 0
5 6 0 2
样例输出3
1 4
数据范围与提示
对于100%的数据,保证给出的图为一棵树,有 n ∈ [1,1e5],a, b ∈ [1, n],c ∈ {0, 1},d ∈ {0, 1, 2}
若操作的边集为 E E E,那么操作次数则为 E E E中度数为奇数的点的个数 / 2 /2 /2,称这种点为端点
对于点 i i i而言
若为奇数,除去与父亲相连的边则变成了偶数,儿子可以两两配对
那么它就孤了下来,成为一条有可能被操作的路径的一端
若为偶数,除去与父亲相连的边则变成了奇数,儿子两两配对后则还剩一个儿子
那么此时 i i i称当一个中转站的作用,衔接着儿子与父亲
反正无论如何它都只可能是一条可能被操作的路径上的一个点,仅此而已
此时赤裸裸的思想,先控制操作数最少,其次控制路径最小
设 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]表示以 i i i为根的子树( i i i与父亲 f a fa fa相连的那条边是否翻转,翻转为 1 1 1,不翻转为 0 0 0)的最小操作数以及最小路径和
再设 w 1 w1 w1表示 i i i与父亲 f a fa fa相连的边需要翻转, w 2 w2 w2表示 i i i与父亲 f a fa fa相连的边不需要翻转
w 1 = m i n ( d p [ v ] [ 0 ] + w 1 , w 2 + d p [ v ] [ 1 ] ) w1=min(dp[v][0]+w1,w2+dp[v][1]) w1=min(dp[v][0]+w1,w2+dp[v][1])
①儿子不需要翻转, i i i与父亲的边已经翻转
② i i i与父亲的边尚未翻转,儿子需要翻转
w 2 = m i n ( w 2 + d p [ v ] [ 0 ] , w 1 + d p [ v ] [ 1 ] ) w2=min(w2+dp[v][0],w1+dp[v][1]) w2=min(w2+dp[v][0],w1+dp[v][1])
①儿子不翻转,父亲也不翻转
②儿子翻转,父亲再翻转回来
#include
#include
using namespace std;
#define MAXN 100005
#define inf 0x3f3f3f3f
struct node {
int opt, len;
node(){
}
node( int Opt, int Len ) {
opt = Opt, len = Len;
}
node operator + ( const node &t ) const {
return node( opt + t.opt, len + t.len );
}
}dp[MAXN][2];
vector < pair < int, int > > G[MAXN];
int n;
node Min( node x, node y ) {
if( x.opt < y.opt ) return x;
else if( x.opt == y.opt && x.len < y.len ) return x;
else return y;
}
void dfs( int u, int fa, int type ) {
node w1 = node( inf, inf ), w2 = node( 0, 0 );
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, flag = G[u][i].second;
if( v == fa ) continue;
dfs( v, u, flag );
node tmp1 = Min( w1 + dp[v][0], w2 + dp[v][1] );
node tmp2 = Min( w1 + dp[v][1], w2 + dp[v][0] );
w1 = tmp1, w2 = tmp2;
}
if( type == 1 || type == 2 )
dp[u][1] = Min( node( w1.opt, w1.len + 1 ), node( w2.opt + 1, w2.len + 1 ) );
else
dp[u][1] = node( inf, inf );
if( type == 0 || type == 2 )
dp[u][0] = Min( node( w1.opt + 1, w1.len ), w2 );
else
dp[u][0] = node( inf, inf );
}
int main() {
scanf( "%d", &n );
for( int i = 1, a, b, c, d;i < n;i ++ ) {
scanf( "%d %d %d %d", &a, &b, &c, &d );
c = ( d == 2 ) ? d : c != d;
G[a].push_back( make_pair( b, c ) );
G[b].push_back( make_pair( a, c ) );
}
dfs( 1, 0, 2 );
printf( "%d %d", dp[1][0].opt >> 1, dp[1][0].len );
return 0;
}