A
题意:给出数组,和一个窗口大小m,在每个区间[i , i + m - 1 ],求最大值maxrating和i的异或,A为所有区间这个值的和,还要求从这个区间开始maxrating为-1,找到大的count就加1,还要求count和i的异或,B为所有区间这个值的和。
A=∑( i=1 , n−m+1) (maxratingi⊕i)
B=∑(i=1 , n−m+1)(counti⊕i)
思路:双端队列实现的滑动窗口,倒着来,每个区间的最大值就是队头元素,比较次数就是队列里的元素个数。
Code;
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 2e7+66;
LL a[AX];
LL deq[AX];
//deque<int>deq;
int main(){
int T;
scanf("%d",&T);
int n , m , k , p , q , r , mod ;
while( T-- ){
scanf("%d%d%d%d%d%d%d",&n,&m,&k,&p,&q,&r,&mod);
for( int i = 1 ; i <= k ; i++ ){
scanf("%d",&a[i]);
}
for( int i = k + 1 ; i <= n ; i++ ){
a[i] = ( 1LL * p * a[i-1] + 1LL * q * i + r ) % mod;
}
int h = 1 , t = 0;
LL A = 0LL , B = 0LL;
for( int i = n ; i ; i -- ){
while( h <= t && a[i] >= a[deq[t]] ) t --;
deq[++t] = i;
if( i + m - 1 <= n ){
while( deq[h] >= i + m ) h++;
A += i ^ ( a[deq[h]] );
B += i ^ ( t - h + 1 );
}
}
printf("%lld %lld\n",A,B);
}
return 0 ;
}
题意:给一个数组,求有多少个严格上升的子序列
思路:
dp[i]表示以i结尾的上升子序列个数,dp[i] = Σdp[j] + 1 a[i] > a[j] .
先离散化下,然后用树状数组维护前面比他小的 结果的和 , 每次加1然后更新dp[i],最后询问最大的数(离散化后最大为n)的结果。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 1e5+66;
const LL MOD = 1e9+7;
LL a[AX];
LL b[AX];
LL c[AX];
map<int,int>mp;
LL n ;
int lowbit(int x){
return x&(-x);
}
void update( LL value , int site){
while( site <= n ){
c[site] = ( c[site] + value ) % MOD;
site += lowbit(site);
}
}
LL getsum(int i){
LL sum = 0;
while( i >= 1 ){
sum = ( sum + c[i] ) % MOD ;
i -= lowbit(i);
}
return sum % MOD;
}
int tot ;
std::vector<int> v;
int get_id( int x ){
//return lower_bound( v.begin() , v.end() , x ) - v.begin() + 1 ;
if( !mp[x] ) mp[x] = ++tot;
return mp[x];
}
int main(){
int T;
scanf("%d",&T);
while( T-- ){
memset( c , 0 , sizeof(c) );
mp.clear();
tot = 0 ;
//v.clear();
scanf("%lld",&n);
for( int i = 1 ; i <= n ; i ++ ){
scanf("%lld",&a[i]); b[i] = a[i];
//v.push_back(a[i]);
}
//sort( v.begin() , v.end() ) ; v.erase( unique(v.begin(),v.end()) , v.end() );
sort( b + 1 , b + n + 1 );
for( int i = 1 ; i <= n ; i++ ) b[i] = get_id(b[i]);
for( int i = 1 ; i <= n ; i++ ) a[i] = get_id(a[i]);
for( int i = 1 ; i <= n ; i++ ){
LL ans = getsum( a[i] - 1 ) % MOD;
update( ans + 1 , a[i] );
}
LL res = getsum( n ) % MOD;
cout << res << endl;
}
return 0 ;
}
C
题意:地上有一个长和宽分别为a,b的矩形,然后两位玩家先后依次在这个矩形内画半径为r的圆,所画的圆可以相切但不能相交,直到某个玩家不能再往其中画圆了则判定此玩家失败,另一玩家获胜。
思路:先手想让后手输,只能找一个没有对称位置的地方画圆,否则后手一直找对称位置,先手就输了,所以矩形的中心就是制胜的地方。先手能放中间就赢。
Code:
#include <bits/stdc++.h>
using namespace std;
int main(){
int T;
scanf("%d",&T);
while( T -- ){
int a , b , r ;
scanf("%d%d%d",&a,&b,&r);
if( a >= 2 * r && b >= 2 * r ){
printf("The first one is winner.\n");
}
else{
printf("The second one is winner.\n");
}
}
return 0;
}
G
题意:给出边的相连关系,以及每个节点的值,有两个操作,一个是将节点的值乘一个数,一个是查询节点及其子节点的乘积和乘积的因子个数。
另外题目说明,数据的素因子最大不超过13。
思路:dfs序+线段树维护区间乘积。
因为要查询每个节点及其子树的乘积,想到线段树可以维护区间的乘积以及约数个数。如果我能够将查询操作转换成区间,
有节点改变,就是单点更新,同时对 操作要乘上的数 求出素因子并计数更新(加)到节点。查询操作就是区间查询。。
那么怎么去构造这样一个区间呢,这就需要用到dfs序。
记录每个节点的dfs序的起始位置,查询就变成了区间查询。
比如:
dfs序:01367245
那么1节点的开始位置就是2,结束位置就是5.
L[1] = 2 ,R[1] = 5
0节点是到最后才遍历完子树,所以:
L[0] = 1 R[0] = 8
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 1e5+66;
const int MOD = 1e9+7;
const int prime[10] = { 2 , 3 , 5 , 7 , 11, 13 };
struct Edge{
int to ;
int next1;
}G[AX];
int cnt ;
int head[AX];
int tot ;
void addEdge( int u , int v ){
G[++tot].to = v ; G[tot].next1 = head[u] ; head[u] = tot ;
}
int val[AX];
int L[AX];
int R[AX];
bool vis[AX];
struct Res{
LL a ;
int fac[10];
Res(){
a = 1LL;
memset( fac , 0 , sizeof(fac) );
}
Res operator + ( const Res &rhs )const{
Res ans ; ans.a = ( rhs.a * a ) % MOD;
for( int i = 0 ; i <= 5 ; i++ ) ans.fac[i] = fac[i] + rhs.fac[i];
return ans ;
}
};
int n;
Res s[AX*20];
void pushUp( int rt ){
s[rt] = s[rt<<1] + s[rt<<1|1];
}
void getFac( Res &p, int v ){
for( int i = 0 ; i <= 5 ; i++ ){
for( ; !(v % prime[i]) ; v /= prime[i] ) p.fac[i] ++;
}
}
void update( int site , int val , int l , int r , int rt ){
if( l == r ){
s[rt].a = (LL)( s[rt].a * (LL)val ) % MOD;
getFac( s[rt] , val );
return ;
}
int mid = ( l + r ) >> 1 ;
if( site <= mid ) update( site , val , l , mid , rt << 1 );
else update( site , val , mid + 1 , r , rt << 1 | 1 );
pushUp(rt);
}
Res query( int L , int R , int l , int r , int rt ){
if( L <= l && R >= r ){
return s[rt] ;
}
Res ans ;
int mid = ( l + r ) >> 1 ;
if( L <= mid ) ans = ans + query( L , R , l , mid , rt << 1 );
if( mid < R ) ans = ans + query( L , R , mid + 1 , r , rt << 1 | 1 );
return ans ;
}
void dfs( int x ){
vis[x] = true;
L[x] = ++cnt;
update(L[x],val[x],1,n,1);
for( int i = head[x] ; ~i ; i = G[i].next1 ){
if( !vis[G[i].to] )
dfs( G[i].to );
}
R[x] = cnt ;
}
int main(){
scanf("%d",&n);
tot = 0;
cnt = 0;
memset( vis , false , sizeof(vis) );
memset( head , -1 , sizeof(head) );
int u , v ;
for( int i = 1 ; i < n ; i++ ){
scanf("%d%d",&u,&v);
u ++ , v ++;
addEdge( u , v );
}
for( int i = 1 ; i <= n ; i++ ){
scanf("%d",&val[i]);
}
dfs(1);
int Q;
scanf("%d",&Q);
char op[20];
int x , y ;
while( Q-- ){
scanf("%s%d",op,&x);
x++;
if( op[0] == 'R' ){
Res res = query( L[x] , R[x] , 1 , n , 1 );
LL num = 1LL;
for( int i = 0 ; i <= 5 ; i++ ){
num = num * (LL)( res.fac[i] + 1 ) % MOD ;
}
printf("%d %d\n",(int)res.a,(int)num);
}else{
scanf("%d",&y);
update( L[x] , y , 1 , n , 1 );
}
}
return 0 ;
}
题意:初始0,有两种A : 2*x+1 或者B:2*x+2,求得到n的方式。
思路:n逆着来得到0即可。。
这题刚开始选择性失明看不到1e9的范围,上来没多想一顿bfs,ME 3次气急败坏就是没反应过来范围。。心态不好。。。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 3e5+66;
char res[AX];
int main(){
int n ;
int tot = 0;
cin >> n ;
while( n ){
if( n % 2 ){
n = ( n - 1 ) / 2 ;
res[tot ++] = 'A';
}else{
n = ( n - 2 ) / 2 ;
res[tot ++] = 'B';
}
}
for( int i = tot - 1 ; i >= 0 ; i-- ){
cout << res[i] ;
}cout << endl;
return 0 ;
}
I
题意:给出几个星球,还有坐标,任意星球的距离是欧几里得距离(直线距离),有的星球间有黑洞,单向的,给出的询问两个星球,输出最短距离。
思路:floyd。黑洞的距离置为0即可。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long
using namespace std;
int n;
map<string,int>mp;
map<int,string>mp1;
struct Node{
LL x , y , z;
};
map<int,Node>co;
int chong[105][105];
double cal( Node a , Node b ){
return ( (double)sqrt((double)(a.x - b.x) * (a.x - b.x) + (double)(a.y-b.y)*(a.y-b.y) + (double)(a.z-b.z)*(a.z-b.z)) );
}
double dis[105][105];
void floyd( ){
for( int k = 1 ; k <= n ; k++ ){
for( int i = 1 ; i <= n ; i++ ){
for( int j = 1 ; j <= n ; j++ ){
dis[i][j] = min( dis[i][j] , dis[i][k] + dis[k][j] );
}
}
}
}
int main(){
int T;
scanf("%d",&T);
int Case = 0 ;
while( T-- ){
co.clear();
mp.clear();
LL x , y , z;
scanf("%d",&n);
string s;
for( int i = 1 ; i <= n ; i++ ){
cin >> s >> x >> y >> z;
mp[s] = i;
co[i] = (Node){ x , y , z };
}
int q;
string s1, s2;
for( int i = 1 ; i <= n ; i++ ){
for( int j = 1 ; j <= n ; j++ ){
dis[i][j] = cal( co[i] , co[j] );
}
}
cin >> q ;
for( int i = 0 ; i < q ; i ++ ){
cin >> s1 >> s2;
dis[mp[s1]][mp[s2]] = 0;
}
floyd();
cin >> q ;
cout << "Case " << ++Case << ':' << endl;
for( int i = 0 ; i < q; i++ ){
cin >> s1 >> s2;
printf("The distance from %s to %s is %.0f parsecs.\n",s1.c_str(),s2.c_str(),dis[mp[s1]][mp[s2]]);
}
}
return 0 ;
}
题意:给两个表,要求输出相似的范围。就是哪一段的字符串相同位置可以不同。
思路:相同的话就直接是1,否则 因为每个字符串不同,给每个字符串一个值,就记录这一段的和,如果和相等说明这一段的字母相同。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 1e6+66;
map<string,int>mp;
int a[AX];
int b[AX];
int res[AX];
int main(){
int T;
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> T;
int n ;
while( T-- ){
cin >> n ;
string s ;
for( int i = 1 ; i <= n ; i++ ){
cin >> s ;
mp[s] = i;
a[i] = i ;
}
for( int i = 1 ; i <= n ; i++ ){
cin >> s;
b[i] = mp[s];
}
int f = 0 ;
int tot = 0 ;
LL suma = 0LL ;
LL sumb = 0LL ;
int l = 0 ;
for( int i = 1 ; i <= n ; i++ ){
if( !f ){
if( a[i] == b[i] ){
res[tot++] = 1 ;
suma = 0LL;
sumb = 0LL;
}else{
suma += a[i] ;
sumb += b[i] ;
f = 1;
l = i ;
}
}else{
suma += a[i];
sumb += b[i];
if( suma == sumb ){
res[tot++] = i - l + 1 ;
suma = 0LL;
sumb = 0LL;
f = 0 ;
}
}
}
for( int i = 0 ; i < tot ; i++ ){
cout << res[i] ;
if( i != tot - 1 ) cout << ' ';
}
cout << endl;
}
return 0 ;
}