A 题 Minecraft Server Bug
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1552
题意 : 给你一串W,L串,你需要找一对W,L,要求W在L之前,能找到多少对。
思路 : 直接遍历字符串 , 用一个变量记录之前出现过多少W了,遇到L就加上这个数量就行。
#include <stdio.h>
int main(){
int n ;
while( scanf( "%d" , &n ) != EOF ) {
long long ans = 0 ;
long long cntW = 0 ;
char str[3] ;
while( n -- ) {
scanf( "%s" , str ) ;
if( str[0] == 'W' ) cntW ++ ;
else ans += cntW ;
}
printf( "%lld\n" , ans ) ;
}
return 0 ;
}
B 题 Beautiful Walls
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1553
题意: 给你一堆unit( 就是墙吧 ? 这游戏没玩过 ) , 每个unit有高度 , 你需要选择一个区间 [ a , b ] , 使这个区间中的unit没有高度相同的。问你有多少种选择的方法。
思路: 枚举区间的终点 , 对于每个终点 , 我们只要知道起点最前面可以是多少就行了。那么如何确定这个最前面的起点,很显然,如果当前枚举的这个高度之前已经出现过了,那么起点一定在上次出现这个高度的unit之后 , 那么我们规定 pre[i] 为和第i个unit高度相同的unit的下标。那么对于以一个终点,那么他的最前面的起点就是max( pre[j] | j < =i ) + 1 了。为什么取pre[j]的最大值? 确定pre[i]的话 , 可以用map或者hash记录下就可以了。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
using namespace std ;
map<int,int> last ;
int main(){
int n ;
while( scanf( "%d" , &n ) != EOF ) {
last.clear() ;
long long ans = 0 ;
int lll = 0 ;
for( int i = 1 ; i <= n ; i ++ ) {
int a ;
scanf( "%d" , &a ) ;
int ll = last[a] ; last[a] = i ;
lll = max( lll , ll ) ;
ans += i - lll ;
}
printf( "%lld\n" , ans ) ;
}
return 0 ;
}
C 题 Lord of Minecraft LCA
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1554
题意: 给你一棵树,问从a点跑到b点,会不会经过和a点同样深度的点,如果b点的深度和a点相同,也算经过。
思路: 其实就是判断a和b是不是祖孙关系,如果是的话,肯定不会经过,不会的话,比较深度就可以了
那么判断a和b是不是祖孙关系,通常就是条件反射求LCA。
出题人给了另外一个思路,就是直接dfs的时候,计算进入每个顶点和离开每个顶点的时间戳,如果a和b是祖孙关系,那么a和b的时间戳区间必然是一个区间包含另一个区间。
下面的代码的话,是用LCA的。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define MAXN 10005
#define MAXM 10005
int n , m , q ;
map<string,int> mp ;
struct Graph{
int head[MAXN] ;
int Index ;
struct Edge{
int to , nex , val ;
Edge(){}
Edge( int _to , int _nex , int _val ):to(_to),nex(_nex),val(_val){}
}edge[MAXM*2] ;
void init(){
memset(head,-1,sizeof(head));
Index = 0 ;
}
void add( int from , int to , int val = 0 ){
edge[Index] = Edge( to , head[from] , val ) ;
head[from] = Index ++ ;
}
}gra;
int degree[MAXN] ;
int E[MAXN*2] ; // dfs的下标
int R[MAXN] ; // 每个时间戳所对应的节点
int H[MAXN*2] ; // E中对应节点的深度
bool vis[MAXN] ; // 某个节点是否被访问
int id ; // E 数组对应的下标 , 初始值为 0
int dis[MAXN] ; // 点u距离根节点的距离(边有边权的情况下)
int Index ;
void dfs( int u , int d , int dist ){ // dfs 时的节点u , 离根节点的距离d
E[++id] = u ; R[u] = id ; H[id] = d ; vis[u] = true ; dis[u] = dist ;
for( int i = gra.head[u] ; ~i ; i = gra.edge[i].nex ){
int ch = gra.edge[i].to ;
if( !vis[ch] ){
dfs( ch , d + 1 , dist + gra.edge[i].val ) ;
E[++id] = u ; H[id] = d ;
}
}
}
int stmin[MAXN*2][21] ; // 注意!!存的是最小值的下标,不是最小值
int d[21] ; // d[i] = 1 << i 用于加快计算速度
// 预处理RMQ
void InitRMQ(){
d[0]=1;
for( int i=1;i<21;i++ )d[i] = d[i-1]<<1 ;
for( int i=1;i<=id;i++ ) stmin[i][0] = i ;
for( int j=1;d[j]<=id;j++ )
for( int i=1;i+d[j]-1<=id;i++ )
stmin[i][j] = H[stmin[i][j-1]]<H[stmin[i+d[j-1]][j-1]]?stmin[i][j-1]:stmin[i+d[j-1]][j-1] ;
}
// 构完树之后直接调用记得可 , 可以解决存在森林的情况
void solve(){
id = 0 ;
memset(vis,false,sizeof(vis));
//dfs(1,0,0);
for( int i = 1 ; i <= Index ; i ++ ) {
if( degree[i] == 0 ) {
dfs( i , 0 , 0 ) ;
break;
}
}
/*
for( int i=1;i<=Index;i++ ){
if(!vis[i]){
while( true ) ;
}
}*/
InitRMQ();
}
// 若LCA == -1 则说明点a和点b在不同的连通块
int LCA( int a , int b ){ // 返回LCA(a,b)
int l = R[a] , r = R[b] ;
if( l > r )swap( l , r ) ;
int k = int( log(double(r-l+1))/log(2.0) );
int lca = H[stmin[l][k]]<H[stmin[r-d[k]+1][k]]?stmin[l][k]:stmin[r-d[k]+1][k] ;
return E[lca] ;
}
int main(){
int casn = 0 ;
while( scanf("%d%d",&n,&q)!=EOF ){
casn ++ ;
//if( casn == 3 ) while( true ) ;
gra.init();
mp.clear() ;
Index = 0 ;
memset( degree , 0 , sizeof(degree) ) ;
for( int i=1;i<=n;i++ ){
char name1[105] , name2[105] ;
scanf( "%s%s" , name1 , name2 ) ;
int ida = mp[name1], idb = mp[name2] ;
if( ida == 0 ) {
ida = mp[name1] = ++Index ;
}
if( idb == 0 ) {
idb = mp[name2] = ++Index ;
}
gra.add(ida,idb,1);
gra.add(idb,ida,1);
degree[ida] ++ ;
}
solve();
int ans = 0 ;
while(q--){
char name1[105] , name2[105] ;
scanf( "%s%s" , name1 , name2 ) ;
int ida = mp[name1] , idb = mp[name2] ;
//if( ida == 0 || idb == 0 ) {
// while( true ) ;
//}
int lca = LCA(ida,idb);
if( lca == ida || lca == idb ) {
continue ;
}
if( dis[ida] <= dis[idb] ) ans ++ ;
//printf("%d\n",dis[a]+dis[b]-2*dis[lca]);
}
printf( "%d\n" , ans ) ;
}
return 0 ;
}
D 题 The Sum of F(x) and G(x)
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1555
题意: 就是模拟两个多项式相加
思路: 数据量很小,怎么搞都行
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int c[30] ;
int main(){
int n , m ;
while( scanf( "%d%d" , &n , &m ) != EOF ) {
memset( c , 0 , sizeof(c) ) ;
while( n -- ) {
int a , b ;
scanf( "%d%d" , &a , &b ) ;
c[b+15] += a ;
}
while( m -- ) {
int a , b ;
scanf( "%d%d" , &a , &b ) ;
c[b+15] += a ;
}
for( int i = 25 ; i >= 0 ; i -- ) {
if( c[i] ) {
printf( "%d %d\n" , c[i] , i - 15 ) ;
}
}
}
return 0 ;
}
E 题 Flandre at Minecraft
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1556
题意 : 等于给你个N*N的01串,你要通过最小的操作数把他变成全0串,你可以有三种操作,
1: 取反一个元素
2. 取前D*K个元素取反
3. 取后D*K个元素取反
D是题目给出的,并保证能被N*N整除,K是你任意选取的,并每次操作可以不一样。
求最小操作数。
思路 : 还没想出来 ... 想出来再补 ...
F 题 Team of Slime
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1557
题意: 给你一个1~N的排列,你要通过一些操作把这个排列变成1,2,...,N这样的有序排列。但是每次操作只能把某个数提到数列的最前面。问最小操作数
思路: 很明显,如果 a[i] > a[j] 并且 i < j , 那么如果最后要让他们变得有序,肯定是要有某次操作把a[j]提到前面去的。那么这样之后,所有小于a[j]都必须往前提一遍,也只需要提一遍(只要从a[j]-1,a[j]-2...,1这样的顺序往前提) , 就能让1...a[j]是有序的在前j个了。那么我们还可以想到,如果a[j]比较小,这样操作完,当有比他大的往前提的时候,这个操作就全白费了,所以我们很容易就可以想到,第一个往前提的就是满足上述关系的最大的一个a[j] ,并且最后答案就是a[j]。那么这样我们只要记录每个元素出现的位置就可以了。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int pos[300005] ;
int main(){
int n;
while( scanf( "%d" , &n ) != EOF ) {
int a ;
for( int i = 1 ;i <= n ;i ++ ) {
scanf( "%d" , &ai ) ;
pos[a] = i ;
}
int ans = 0 ;
int Min = n ;
for( int i = n ; i >= 1 ; i -- ) {
if( pos[i] > Min ) {
ans = i ;
break;
}else{
Min = min( Min , pos[i] ) ;
}
}
printf( "%d\n" , ans ) ;
}
return 0 ;
}
G题 Racing Cheat 计算几何 + 最短路
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1558
题意 : 起点是( 0 , 0 ) , 终点是( n , n ) , 然后给你n条线段作为障碍物,求从起点到终点的最短路。
思路: 把起点终点还是线段的端点都处理到邻接矩阵中,两点之间没有线段阻拦的就连上权值为长度的边,跑遍flody就行了
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std ;
#define POW(x) ((x)*(x))
#define eps 1e-8
#define zero(x) (((x)>0?(x):-(x))<eps)
#define INF 1e16
int n , m , peo ;
struct Point{
double x, y ;
Point(){}
Point( double X ,double Y ):x(X),y(Y){}
}p[305];
struct Line{
Point a , b ;
Line(){}
Line(Point A , Point B):a(A),b(B){}
}l[105];
double Map[305][305] ;
double Dist( Point a , Point b ){
return sqrt( POW(a.x-b.x) + POW(a.y-b.y));
}
double xmult(Point p1,Point p2,Point p0){
return(p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
int opposite_side(Point p1,Point p2,Line l){
return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)<-eps;
}
int intersect_ex(Line u,Line v){
return opposite_side(u.a,u.b,v) && opposite_side(v.a,v.b,u);
}
int cntP ;
int main(){
double ll ;
while( scanf("%lf%d",&ll,&m) != EOF ){
cntP = 0 ;
n = 2 ;
p[++cntP] = Point( 0 , 0 ) ;
p[++cntP] = Point( ll , ll ) ;
for( int i=1;i<=m;i++ ){
double a , b , c , d ;
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
p[++cntP] = Point(a,b);
p[++cntP] = Point(c,d);
l[i] = Line(p[cntP-1],p[cntP]);
}
int N = n + 2 * m ;
for( int i=1;i<=N;i++ ){
for( int j=1;j<=N;j++ )
Map[i][j] = INF ;
Map[i][i]=0;
}
for( int i=1;i<=N;i++ ){
for( int j=i+1;j<=N;j++ ){
bool ok = true ;
for( int k=1;k<=m;k++ ){
if(intersect_ex(Line(p[i],p[j]),l[k])){
ok = false ;break;
}
}
if( ok ){
Map[i][j] = Map[j][i] = Dist(p[i],p[j]);
}
}
}
for( int k=1;k<=N;k++ )
for( int i=1;i<=N;i++ )
for( int j=1;j<=N;j++ )
if( i!=j && j!=k && i!=k && Map[i][k] + Map[k][j] < Map[i][j] )
Map[i][j] = Map[i][k] + Map[k][j] ;
printf("%.2lf\n", Map[1][2] );
}
return 0 ;
}
H题 Jump to the Top of Mountain dfs
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1559
题意: 给你一张图,每个点都有高度,外围又一圈高度为0的地方,你可以选择外围任意的点为起点,你只能往四个方向走,并且你当前处于高度x的话,你不能走到高度大于x+1的格子,问你能不能走到最高峰。( PS : 貌似最高峰有多个 , 只要能走到其中一个就可以了 )
思路: 图很小,只要从最高点往外dfs,看看能不能走到边界就行了
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int n , m ;
int Max ;
int mp[105][105] ;
bool vis[105][105] ;
int dir[4][2] = { 1 , 0 , -1 , 0 , 0 , 1 , 0 , -1 } ;
bool ok ;
void dfs( int x , int y ) {
if( x < 0 || x > n || y < 0 || y > m ) {
ok = true ;
return ;
}
vis[x][y] = true ;
for( int i = 0 ; i < 4 ; i ++ ) {
int nex = x + dir[i][0] , ney = y + dir[i][1] ;
if( vis[nex][ney] ) continue ;
if( mp[nex][ney] + 1 >= mp[x][y] ) {
dfs( nex , ney ) ;
if( ok ) return ;
}
}
}
int main(){
while( scanf( "%d%d" , &n , &m ) != EOF ) {
Max = 0 ;
memset( mp , 0 , sizeof(mp) ) ;
for( int i = 1 ;i <= n ;i ++ ) {
for( int j = 1 ; j <= m ; j ++ ) {
scanf( "%d" , &mp[i][j] ) ;
Max = max( Max , mp[i][j] ) ;
}
}
memset( vis , false , sizeof(vis) ) ;
ok = false ;
for( int i = 1 ; i <= n && ok == false ; i ++ ) {
for( int j = 1 ; j <= m && ok == false ; j ++ ) {
if( mp[i][j] == Max ) {
dfs( i , j ) ;
}
}
}
puts( ok?"YES":"NO") ;
}
return 0 ;
}
I 题 Let Slimes Grow Up 线段树
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1560
题意 : 给你n个数,有两个操作,一个是区间[a,b]所有数加上c,另一个是查询[a,b]区间中的方差大小。
思路: 主要只要求解出 [a,b] 区间中 Σ( x ^ 2 ) 和 Σ ( x ) 就可以了。求Σ(x) 直接线段树区间更新,区间查询即可。Σ( x^2 ) 在更新的时候要通过( x + c ) ^ 2 = ( x ^ 2 + 2 * c * x + c ^2 ) 这个公式维护即可。
嘛...这题貌似因为是精度问题...一直过不了... 下面是代码。在OJ上AC不了
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define MAXN 100005
long long sum1[MAXN<<2] , sum2[MAXN<<2] , lazy[MAXN<<2] ;
void pushup( int rt ) {
sum1[rt] = sum1[rt<<1] + sum1[rt<<1|1] ;
sum2[rt] = sum2[rt<<1] + sum2[rt<<1|1] ;
}
void build( int l , int r , int rt ) {
lazy[rt] = 0 ;
if( l == r ) {
scanf( "%lld" , &sum1[rt] ) ;
sum2[rt] = sum1[rt] * sum1[rt] ;
return ;
}
int m = ( l + r ) >> 1 ;
build( lson ) ;
build( rson ) ;
pushup( rt ) ;
}
void pushdown( int rt , int len ) {
lazy[rt<<1] += lazy[rt] ;
lazy[rt<<1|1] += lazy[rt] ;
sum2[rt<<1] += 2 * lazy[rt] * sum1[rt<<1] + lazy[rt] * lazy[rt] * ( len - len / 2 ) ;
sum2[rt<<1|1] += 2 * lazy[rt] * sum1[rt<<1|1] + lazy[rt] * lazy[rt] * ( len / 2 ) ;
sum1[rt<<1] += lazy[rt] * ( len - len / 2 ) ;
sum1[rt<<1|1] += lazy[rt] * ( len / 2 ) ;
lazy[rt] = 0 ;
}
void update( int l , int r , int rt , int L , int R , int add ) {
if( L <= l && R >= r ) {
sum2[rt] += ( r - l + 1 ) * add * add + 2 * add * sum1[rt] ;
sum1[rt] += ( r - l + 1 ) * add ;
lazy[rt] += add ;
return ;
}
if( lazy[rt] ) {
pushdown( rt , r - l + 1 ) ;
}
int m = ( l + r ) >> 1 ;
if( L <= m ) {
update( lson , L , R , add ) ;
}
if( R > m ) {
update( rson , L , R , add ) ;
}
pushup( rt ) ;
}
long long query1( int l , int r , int rt , int L , int R ) {
if( L <= l && R >= r ) {
return sum1[rt] ;
}
if( lazy[rt] ) pushdown( rt , r - l + 1 ) ;
int m = ( l + r ) >> 1 ;
long long ans = 0 ;
if( L <= m ) {
ans += query1( lson , L , R ) ;
}
if( R > m ) {
ans += query1( rson , L , R ) ;
}
return ans ;
}
long long query2( int l , int r , int rt , int L , int R ) {
if( L <= l && R >= r ) {
return sum2[rt] ;
}
if( lazy[rt] ) pushdown( rt , r - l + 1 ) ;
int m = ( l + r ) >> 1 ;
long long ans = 0 ;
if( L <= m ) {
ans += query2( lson , L , R ) ;
}
if( R > m ) {
ans += query2( rson , L , R ) ;
}
return ans ;
}
int main(){
int n , m ;
long long s1 , s2 ;
double ave , ans ;
while( scanf( "%d%d" , &n , &m )!= EOF ) {
build( 1 , n , 1 ) ;
while( m -- ) {
int op ;
scanf( "%d" , &op ) ;
if( op == 1 ) {
int a , b , c ;
scanf( "%d%d%d" , &a , &b , &c ) ;
a ++ ; b ++ ;
update( 1 , n , 1 , a , b , c ) ;
}else if( op == 2 ) {
int a , b ;
scanf( "%d%d" , &a , &b ) ; a ++ ; b ++ ;
s1 = query1( 1 , n , 1 , a , b ) ;
s2 = query2( 1 , n , 1 , a , b ) ;
ave = s1 * 1.0 / ( b - a + 1 ) ;
ans = ( s2 - 2 * ave * s1 + ave * ave * ( b - a + 1 ) ) / ( b - a + 1 ) ;
printf( "%lld\n" , (long long)ans ) ;
}
}
}
return 0 ;
}
J题 Set Time
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1561
题意 : 就是计算s秒时候是处在第几天和第几个月第几年
思路: 嘛...我可耻的拖了zju的模板...
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <math.h>
using namespace std ;
int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
class Date {
public:
//判闰年
inline static bool isLeap(int year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
int year, month, day;
//判合法性
inline bool isLegal() const {
if (month <= 0 || month > 12) {
return false;
} if (month == 2) {
return day > 0 && day <= 28 + isLeap(year);
}
return day > 0 && day <= days[month - 1];
}
//比较日期大小
inline int compareTo(const Date & other) const {
if (year != other.year) {
return year - other.year;
}
if (month != other.month) {
return month - other.month;
}
return day - other.day;
}
//返回指定日期是星期几 0 (Sunday) ... 6 (Saturday)
inline int toWeekday() const {
int tm = month >= 3 ? (month - 2) : (month + 10);
int ty = month >= 3 ? year : (year - 1);
return (ty + ty / 4 - ty / 100 + ty / 400 + (int)(2.6 * tm - 0.2) + day) % 7;
}
//日期转天数偏移
inline int toInt() const {
int ret = year * 365 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
days[1] += isLeap(year);
for (int i = 0; i < month - 1; ret += days[i++]);
days[1] = 28;
return ret + day;
}
//天数偏移转日期
inline void fromInt(int a) {
year = a / 146097 * 400;
for (a %= 146097; a >= 365 + isLeap(year); a -= 365 + isLeap(year), year++);
days[1] += isLeap(year);
for (month = 1; a >= days[month - 1]; a -= days[month - 1], month++);
days[1] = 28;
day = a + 1;
}
}start,finish;
int main(){
int n ;
start.year = 1970 , start.month = 1 , start.day = 1 ;
int start_shift = start.toInt () ;
while( scanf( "%d" , &n ) != EOF ) {
int day = n / ( 60 * 60 * 24 ) ;
int finish_shift = start_shift + day ;
finish.fromInt( finish_shift ) ;
printf( "year: %d\n" , finish.year - 1970 + 1 ) ;
printf( "month: %d\n" , ( finish.year - 1970 ) * 12 + finish.month ) ;
printf( "day: %d\n" , day + 1 ) ;
}
return 0 ;
}
K题 Brick Game 博弈
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1562
题意: ... 题意在比赛两天后...才知道真正的题意 ... T。T读错了两遍...
题意是给你n堆石子,你有两种操作:
1. 可以选择1堆石头减少石头的个数
2.可以选择1堆石头个数为0的石堆,删除后面所有的石堆( 注意是删除,不是清零,清零石堆还是存在的 ,删除是石堆就不存在了 )
谁把最后一个石堆删掉,谁就赢。
举个例子 : 0000 这个应该是先手胜 ...
思路 : 直接根据PN状态记忆化搜索即可... 数据量不大
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int dp[1000005][6] ;
int ten[9] ;
int solve( int n , int len ) {
if( dp[n][len-1] != -1 ) return dp[n][len-1] ;
if( n < ten[len] ) {
return dp[n][len] = 1 ;
}
int t = n ;
for( int i = 1 ; i <= len ; i ++ ) {
int tmp = t % 10 ; t /= 10 ;
if( tmp > 0 ) {
for( int j = 1 ; j <= tmp ; j ++ ) {
if( solve( n - j * ten[i] , len ) == 0 ) {
return dp[n][len-1] = 1 ;
}
}
}else{
if( solve( t , len - i ) == 0 ) {
return dp[n][len-1] = 1 ;
}
}
}
return dp[n][len-1] = 0 ;
}
int main(){
ten[1] = 1 ;
for( int i = 2 ; i <= 7 ; i ++ ) ten[i] = ten[i-1] * 10 ;
memset( dp , -1 , sizeof(dp) ) ;
int n ;
char str[15] ;
while( scanf( "%s" , &str ) != EOF ) {
int len = strlen( str ) ;
sscanf( str , "%d" , &n ) ;
if( n == 0 ) {
puts( "Hahaha" ) ;
continue ;
}
puts( solve( n , len ) ? "Hahaha":"Papapa" ) ;
}
return 0 ;
}