【FJWC】day2简要题解

sro fjzzq2002

1.直径

xjb构造三叉树一定有解
时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include
using namespace std;
const int N=5005;

int k,n,a,b,c;
int u[N],v[N],tot,pre;

inline void fd()
{
   for(a=1;a<5000;++a)
	 for(b=a;a+b<5000 && a*b<=k;++b) 
	   if((k-a*b)%(a+b)==0){
	     if((k-a*b)/(a+b)<=5000-a-b-4) 
		   {c=(k-a*b)/(a+b);
		   return;}	
	   }
}

int main(){
	freopen("diameter.in","r",stdin);
	freopen("diameter.out","w",stdout);
	int i,j;
	scanf("%d",&k);fd();
    u[++tot]=(n=1);v[tot]=++n;pre=n;
	for(i=1;i<=a;++i){u[++tot]=pre;v[tot]=++n;}
	u[++tot]=1;v[tot]=++n;pre=n;
	for(i=1;i<=b;++i){u[++tot]=pre;v[tot]=++n;}
	if(c){
		u[++tot]=1;v[tot]=++n;pre=n;
		for(i=1;i<=c;++i) {u[++tot]=pre;v[tot]=++n;}
	}
    printf("%d\n",n);
    for(i=1;i<n;++i) printf("%d %d 1\n",u[i],v[i]);
    fclose(stdin);fclose(stdout);
    return 0;
}

2.定价

用你喜爱的数据结构维护:

  • 每个二进制位上1出现的连续区间
  • 在位置 i i i找到最高的一位 j j j满足第 i − 1 i-1 i1位置这位为1且位置 i i i这位不能再为1,并查找更高位最近的一个可以变成1的0,并把所有小的位变成0。

似乎可以暴力艹标算。
代码咕咕咕。

std:

#include 

using namespace std;

const int md = 1e9 + 7;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (long long) x * y % md;
}

inline int power(int x, int y) {
  int res = 1;
  for (; y; y >>= 1, x = mul(x, x)) {
    if (y & 1) {
      res = mul(res, x);
    }
  }
  return res;
}

template<typename T>
class heap {
 private:
  priority_queue<T> p, q;

 public:
  inline void push(T x) {
    p.push(x);
  }

  inline void pop(T x) {
    q.push(x);
  }

  inline void clear() {
    while (!p.empty()) {
      p.pop();
    }
    while (!q.empty()) {
      q.pop();
    }
  }

  inline bool empty() {
    return p.size() == q.size();
  }

  inline T top() {
    while (!q.empty() && p.top() == q.top()) {
      p.pop();
      q.pop();
    }
    return p.top();
  }
};

#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}

int main() {
  FO(price)
  ios::sync_with_stdio(false);
  cin.tie(0);
  int n, m, q;
  cin >> n >> m >> q;
  unordered_map<int, set<pair<int, int>>> columns;
  vector<set<int>> board(n);
  int last = 0;
  while (q--) {
    int type;
    cin >> type;
    if (type == 1) {
      int x, y;
      cin >> x >> y;
      y = m - y; --x;
      if (board[x].count(y)) {
        board[x].erase(y);
        auto it = columns[y].lower_bound(make_pair(x, 0));
        if (it == columns[y].end() || it->first != x) {
          --it;
        }
        int l = it->first, r = it->second;
        columns[y].erase(it);
        if (l != x) {
          columns[y].insert(make_pair(l, x - 1));
        }
        if (r != x) {
          columns[y].insert(make_pair(x + 1, r));
        }
      } else {
        board[x].insert(y);
        int l = x, r = x;
        {
          auto it = columns[y].lower_bound(make_pair(x, x));
          while (it != columns[y].end() && it->first == r + 1) {
            r = it->second;
            columns[y].erase(it++);
          }
        }
        {
          auto it = columns[y].lower_bound(make_pair(x, x));
          if (it != columns[y].begin()) {
            --it;
            while (it->second == l - 1) {
              l = it->first;
              if (it == columns[y].begin()) {
                columns[y].erase(it);
                break;
              }
              columns[y].erase(it--);
            }
          }
        }
        columns[y].insert(make_pair(l, r));
      }
    } else {
      heap<pair<int, int>> candidates;
      set<int> cur;
      int ans = 0;
      int sum = 0;
      for (int i = 0; i < n; ++i) {
        int largest = -1;
        while (!candidates.empty() && -candidates.top().first == i) {
          largest = max(largest, candidates.top().second);
          candidates.pop(candidates.top());
        }
        bool not_found = false;
        while (true) {
          auto it = board[i].upper_bound(largest);
          if (it == board[i].end()) {
            not_found = true;
            break;
          }
          while (!cur.empty() && *cur.begin() < *it) {
            int x = i, y = *cur.begin();
            sub(sum, power(2, y));
            cur.erase(y);
            --x;
            auto it = columns[y].lower_bound(make_pair(x, 0));
            if (it == columns[y].end() || it->first != x) {
              --it;
            }
            if (it->second != x) {
              candidates.pop(make_pair(-it->second - 1, y));
            }
          }
          if (cur.empty() || *cur.begin() != *it) {
            int x = i, y = *it;
            add(sum, power(2, y));
            cur.insert(y);
            auto it = columns[y].lower_bound(make_pair(x, 0));
            if (it == columns[y].end() || it->first != x) {
              --it;
            }
            candidates.push(make_pair(-it->second - 1, y));
            break;
          } else {
            largest = *it;
          }
        }
        if (not_found) {
          ans = -1;
          break;
        }
        add(ans, sum);
      }
      cout << ans << "\n";
      if (ans != -1) {
        last = ans;
      }
    }
  }
  return 0;
}


3.排序

考虑模拟出这个算法进行 k k k轮(即外层的i循环到k)时的序列,之后再暴力模拟零散的步。

打表找规律,观察每个数位置的变化:

1 9 5 4 8 7 2 3 6 
1 2 9 5 8 7 4 3 6 
1 2 3 9 8 7 5 4 6 
1 2 3 4 9 8 7 5 6 
1 2 3 4 5 9 8 7 6 
1 2 3 4 5 6 9 8 7 
1 2 3 4 5 6 7 9 8 
1 2 3 4 5 6 7 8 9 
1 2 3 4 5 6 7 8 9 

变化规律(设 v i v_i vi表示数字 i i i位置前面 < i <i <i的数字个数):

v i v_i vi i i i位置保持不动,从第 v i + 1 v_i+1 vi+1到第 i − 1 i-1 i1 i i i逐个遍历它后面所有比它小的数的位置,第 i i i轮回归位置 i i i后保持不动。

其实也很好证明:

在第 i i i轮时,维护从位置 i i i开始下表递增的递减序列。
i i i轮结束时,数列整体的变换相当于递减序列中所有数在位置上循环右移了一位(末端的 i i i回到了位置 i i i)

而前 v i v_i vi i i i前面都有比它小的数,不可能进入递减序列,所以位置不动。
同时在这 v i v_i vi轮变化中, i i i位置后面的数和 i i i的相对大小保持不变,而 i i i由于前面没有比它小的数所以会一直呆在递减序列中直到回到位置 i i i并在此之前逐个右移遍历后面所有比它小的数的位置。

线段树维护逆序对即可。

a i = n − i + 1 a_i=n-i+1 ai=ni+1的特判中
j = i + 1 j=i+1 j=i+1写成了 j = i + 2 j=i+2 j=i+2,成功挂掉10分。。。

#include
#include
#include
#include
#include
using namespace std;
const int N=1e6+10;

int n,a[N],m,cnt[35],cot,ans;
int bn[35],mst[35];

char cp,buf[N],wbuf[N],os[100];
int p1,p2,p3;
inline char gc(){
	if(p1==p2) p1=0,p2=fread(buf,1,N,stdin);
	return p1==p2?EOF:buf[p1++];
}

inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
}

inline void wchar(char x)
{
	if(p3==N) fwrite(wbuf,1,N,stdout),p3=0;
	wbuf[p3++]=x;
}

inline int fd(int x)
{
	if(!x) return -1;
	int l=0,r=30,mid,y;
	for(;l<=r;){mid=(l+r)>>1;(x>=(1<<mid))?(l=(y=mid)+1):(r=mid-1);}
	return y;
}

void chg(int x,int y,int op)
{
	x=fd(x);if(x==-1) return;
	if(op){
		if((a[y]>>x)&1) bn[x]++;else{mst[x]++;ans|=(1<<x);}
	}else{
		if((a[y]>>x)&1) bn[x]--;else{mst[x]--;if((!mst[x])) ans^=(1<<x);}
	}
	
}

inline void prit()
{
	int i;
	for(i=0;i<=30;++i) if((bn[i] && mst[i])) break;
	if(i<=30){
	  wchar('-');wchar('1');
	}
	else{
		int x=ans,re=0;
		for(;(!re)||(x);x/=10) os[++re]='0'+(x%10);
		for(;re;--re) wchar(os[re]);
	}
	if(m>1) wchar('\n');
}

int main(){
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	int i,x,y,l,r,mid;
	rd(n);for(i=1;i<=n;++i) rd(a[i]);
	for(i=2;i<=n;++i) chg(a[i-1]^a[i],i,1);
	rd(m);m++;prit();m--;
	for(;m;--m){
		rd(x);rd(y);
		if(x>1) chg(a[x]^a[x-1],x,0);
		if(x<n) chg(a[x]^a[x+1],x+1,0);
		a[x]=y;
		if(x>1) chg(a[x]^a[x-1],x,1);
		if(x<n) chg(a[x]^a[x+1],x+1,1);
		prit();
	}
	if(p3) fwrite(wbuf,1,p3,stdout);
	fclose(stdin);fclose(stdout);
	return 0;
}


总结

T3还是细节问题,且浪费了很久找了许多无用的性质(没有直接打表)

考试的时候前半段时间安排不紧凑,导致做完T1再肝了T3后T2连暴力都没打。
实际上有几位人赢都T2都暴力艹标算 AC了…十分不划算

所以以后遇到时限5s+的题都可以信仰暴力?

你可能感兴趣的:(test,线段树,找规律)