A
题意:n给人围成一圈,有2^k 个面具,编号0-2^k -1 ,问分配方法,满足相邻两个人分配的面具编号:~(i^j) (异或且非)为正数,可以重复分配面具
思路:题目可以转化成: 每个数x都只有一个不能相邻的数2^k - x, 求出所有满足条件的组合。
只有一个人肯定是2^k 个面具随便放;
两个人时就是2^k * (2 ^ k - 1 )种;
那么n >= 3 时,我们考虑:第一个与n-1 个位置的关系。
①当1位置的数 != n - 1 位置的数
a( 1 )!= a(n-1)
那么 第n位限制条件有两个数不能放,所以 dp[n] = dp[n-1] * ( 2 ^ k - 2 )
但是这样考虑会多算一种情况,即 dp[n-1] 内已经包含了 a(1) == a(n-1)的情况。(即使首尾相等,仍然能够相接)。
因此,a(1) != a(n-1) && a(1) 与 a(n-1) 能相接的情况则是dp[n-1] - dp[n-2] * 1 (不能相接就是n-2个人添加第n-1个人时,a(n-1) == 2 ^k - a(1) ),
假如a(1) 与a(n-1) 不能相接(a(n-1) == 2^k - a(1)),那么我们就要继续考虑a[n-2],a(n-2)与a(n-1) 不相同的话,就是dp[n-2] - dp[n-3] ,相同就继续往下考虑,直到最后dp[n-1] - dp[n-2] + dp[n-2] - dp[n-3] +…- dp[1].
得到准确的情况数是:dp[n-1] - dp[1]
所以:( dp[n-1] - 2 ^ k ) * ( 2 ^ k - 2 )
②a(1) == a(n-1)
结果是:dp[n-2] * ( 2 ^ k - 1)
因此,总共的情况是:dp[n]= (dp[n-2](2^k-1) + (dp[n-1] - 2^k) (2^k-2))
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const ll MOD = 1e9 + 7 ;
const int MAXN = 1000076;
ll dp[MAXN];
ll quick( ll a , ll b ){
ll ans = 1ll ;
while( b ){
if( b & 1 ){
ans = ( ans * a ) % MOD ;
}
b >>= 1 ;
a = ( a * a ) % MOD ;
} return ans ;
}
int main(){
int T ;
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0) ;
cin >> T;
ll n , k ;
while( T-- ){
cin >> n >> k ;
dp[1] = quick(2,k)%MOD;
dp[2] = ( quick(2,k) * ( ( quick(2,k)+MOD-1 ) % MOD ) % MOD ) % MOD ;
for( int i = 3 ; i <= n ; i++ ){
dp[i] = ( dp[i-2] * (quick(2,k)+MOD-1)%MOD + (( dp[i-1] - quick(2,k) + MOD ) % MOD ) * ( quick(2,k) - 2 + MOD ) % MOD ) % MOD ;
}
cout << dp[n] << endl;
}
return 0 ;
}
F
题意:给n个框架,每个给k个坐标,问最长的连续出现的坐标值长度。
思路:map直接记录就行了,只不过需要注意的是同一个框架会出现相同坐标。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
typedef pair<int,int>P;
map<P, int> mp ;
map<P, int> num ;
int main(){
int T;
scanf("%d",&T);
while( T-- ){
int k ;
mp.clear();
num.clear();
int n ;
scanf("%d",&n);
int x , y ;
int res = 0 ;
for( int i = 1 ; i <= n ; i++ ){
scanf("%d",&k);
for( int j = 0 ; j < k ; j++ ){
scanf("%d%d",&x,&y);
if( mp[(P(x,y))] == i ) continue;
if( mp[P(x,y)] == i - 1 ){
num[P(x,y)] ++ ;
res = max( res , num[P(x,y)] ) ;
}else{
num[P(x,y)] = 1 ;
}
mp[P(x,y)] = i ;
}
}
printf("%d\n",res);
}
return 0 ;
}
H
题意:阅读L,R区间内的书可以获得知识量:
a[L] * ( R - L + 1 ) + a[L+1] * ( R - L ) ….. + a[R] * 1
给两个操作1 , L ,R ,查询看L,R页的书能够获得多少知识
2 , L , R : 将L位置的值更改为R
思路:两个树状数组,一个维护长度为n-i+1,以i为左边界,n右边界的知识和。
一个维护前缀和。
当查询L,R内的知识量时,首先用第一个树状数组求出的是a[L]* ( n - L + 1 ) + …+a[R] * ( n - R + 1 ) , 第二个树状数组求出的是 a[L] + ….+ a[R] ,
但是我们需要查询a[L] * ( R - L + 1 ) + a[L+1] * ( R - L ) ….. + a[R] * 1
可以看出中间相差 n - R
那么结果就是:第一个树状数组的结果 - ( n - r ) * 第二个树状数组的结果。
更新时,需要查询l位置原来的值tmp,然后加上r-tmp更新之。
Code:
#include <bits/stdc++.h>
#define LL unsigned long long
using namespace std ;
const int AX = 1e5+66;
LL c[AX];
LL c1[AX];
int lowbit( int x ) {
return x & (-x) ;
}
void update( int site , LL val ){
while( site < AX ){
c[site] += val ;
site += lowbit(site) ;
}
}
LL query( int site ){
LL ans = 0ULL ;
while( site > 0 ){
ans += c[site];
site -= lowbit(site);
}
return ans ;
}
void update1( int site , LL val ){
while( site < AX ){
c1[site] += val ;
site += lowbit(site) ;
}
}
LL query1( int site ){
LL ans = 0ULL ;
while( site > 0 ){
ans += c1[site];
site -= lowbit(site);
}
return ans ;
}
int main(){
int n , q ;
LL x ;
scanf("%d%d",&n,&q);
for( int i = 1 ; i <= n ; i++ ){
scanf("%lld",&x);
update( i , 1ULL * ( n - i + 1 ) * x );
update1( i , x ) ;
}
int op ;
int l , r ;
while( q-- ){
scanf("%d%d%d",&op,&l,&r);
if( op == 1 ){
LL ans = query( r ) - query( l - 1 ) ;
LL ans1 = 1ULL * ( n - r ) * ( query1( r ) - query1( l - 1 ) ) ;
printf("%lld\n",ans - ans1);
}else{
LL tmp = query1( l ) - query1( l - 1 ) ;
update( l , 1ULL * ( r - tmp ) * ( n - l + 1 ) ) ;
update1( l , 1ULL * ( r - tmp ) ) ;
}
}
return 0 ;
}
思路:既然迷宫花费最小,那么任意两点可到达,就是最大生成树。然后转化为树上任意两点之间的距离,用LCA处理,这里用的是离线的Tanjan算法。
Code:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 1e6+666;
const int MAXN = 1e6;
struct Node{
int u ;
int v ;
int w ;
bool operator < ( const Node &a ) const{
return a.w < w;
}
}e[AX];
int n , m ;
int tot ;
int pre[AX];
int fa[AX];
int vis[AX];
int ans[AX];
int dis[AX];
struct node{
int to , w;
node( int a , int b ){
to = a; w = b ;
}
};
std::vector<int> vec[AX];
std::vector<node> Q[AX];
void addedge( int u , int v , int w ) {
e[tot].u = u ; e[tot].v = v ; e[tot++].w = w;
}
void init(){
memset( ans , 0, sizeof(ans) ) ;
memset( dis , 0 , sizeof(dis) ) ;
memset( vis , 0 , sizeof(vis) ) ;
for( int i = 1 ; i <= MAXN ; i++ ){
pre[i] = i ;
fa[i] = i ;
vec[i].clear();
Q[i].clear();
}
}
int find( int x ){
return x == pre[x] ? pre[x] : pre[x] = find(pre[x]);
}
void mix( int x , int y ){
int xx = find(x) ;
int yy = find(y) ;
if( xx != yy ){
pre[yy] = xx ;
}
}
void Kruscal(){
sort( e , e + tot );
for( int i = 0 ; i < tot ; i++ ){
if( find(e[i].u) != find(e[i].v) ){
mix( e[i].u , e[i].v ) ;
vec[e[i].u].push_back(e[i].v);
vec[e[i].v].push_back(e[i].u);
}
}
}
int find_fa( int x ){
return x == fa[x] ? fa[x] : fa[x] = find_fa( fa[x] ) ;
}
void Tarjan( int x , int f ){
fa[x] = x ;
vis[x] = 1 ;
for( int i = 0 ; i < vec[x].size() ; i++ ){
if( vec[x][i] == f ) continue;
int v = vec[x][i] ;
dis[v] = dis[x] + 1 ;
Tarjan( v , x );
}
for( int i = 0 ; i < Q[x].size() ; i++ ){
int v = Q[x][i].to;
if(vis[v]){
ans[Q[x][i].w]= dis[x] + dis[v] - 2 * dis[find_fa(v)] ;
}
}
fa[x] = f ;
}
int main(){
scanf("%d%d",&n,&m);
char op[5];
int d , r ;
tot = 0 ;
init();
for( int i = 0 ; i < n ; i++ ){
for( int j = 0 ; j < m ; j++ ){
scanf("%s%d%s%d",op,&d,op,&r);
int id = i * m + j + 1 ;
if( i < n ){
addedge( id , ( i + 1 ) * m + j + 1 , d );
}
if( j < m ){
addedge( id , id + 1 , r );
}
}
}
Kruscal();
int q;
scanf("%d",&q);
int x1 , x2 , y1 , y2 ;
for( int i = 1 ; i <= q ; i++ ){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1-- ; x2--;
int id1 = x1 * m + y1 ;
int id2 = x2 * m + y2 ;
Q[id1].push_back(node(id2,i));
Q[id2].push_back(node(id1,i));
}
dis[1] = 0 ;
vis[1] = 1 ;
Tarjan(1,0);
for( int i = 1 ; i <= q ; i++ ){
printf("%d\n",ans[i]);
}
return 0 ;
}