2021牛客暑假多校1-10总结

目录

第十场

H题

  • 思路

相邻的全部涂成不同的就好了。

  • 代码
奇数个1,涂0;偶数个1,涂1

F题

  • 思路
    出栈入栈可以搞成一棵树,只要树每层颜色不同就可以,所以贪心的放让每层的都不一样。
  • 代码
#include<bits/stdc++.h>

const int N = 1e6 + 5;
const int mod = 998244353;

std::vector<int> G[N];
int vis[N], ans[N];

void solve() {
	int n; std::cin >> n;
	std::string s; std::cin >> s;
	int cnt = 0; // 初始深度为0
	std::vector<int> record;
	record.emplace_back(0);
	for (int i = 0; i < 2 * n; i++) {
		if (s[i] == '(') { 
            ++cnt;// 节点编号加1
			G[record.back()].emplace_back(cnt);
			record.emplace_back(cnt);
		} else {
			record.pop_back(); // 回到上一个节点
		}
	}
	for (int i = 1; i <= n; i++) {
		int x; std::cin >> x;
		++vis[x];
	}
	std::priority_queue<std::pair<int, int> > que;
	for (int i = 1; i <= n; i++) {
		if (vis[i]) {
			que.push({vis[i], i});
		}
	}
	for (int i = 0; i <= n; i++) {
		if (G[i].size() > que.size()) {
			std::cout << "NO\n"; return ;
		}
		std::vector<std::pair<int, int> > buffer; // 暂时缓存
		for (auto v : G[i]) { // 这一层放不同的数字
			ans[v] = que.top().second;
			buffer.emplace_back(que.top());
			que.pop();
		}
		for (auto v : buffer) {
			if (v.first - 1 > 0) {
				que.push({v.first - 1, v.second});
			}
		}
	}
	std::cout << "YES\n";
	for (int i = 1; i <= n; i++) {
		std::cout << ans[i] << " \n"[i == n];
	}
}


int main() {
	std::cin.sync_with_stdio(false), std::cin.tie(nullptr);
	int T = 1; //std::cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

A题

  • 思路
    看代码中的注释
  • 代码
#include<iostream>
#include<cstring>
#include<unordered_map>
#include<vector>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int P = 13331,N = 1e5+10;
string s[N];
int n,ans[N],l[N],r[N];
ull sum[N]; //每个字符串1到l【i】的哈希前缀
unordered_map<ull , vector<int> >mp;//存储每种哈希前缀对应的字符串
int main()
{
    cin>>n;
    IOS;
    for(int i=1;i<=n;i++){
        cin>>s[i];r[i] = s[i].size();
        sum[i] = s[i][0];
        mp[sum[i]].push_back(i);
    }
    //当i==n时,最小前缀数量为mp的siz,考虑倒着循环,每减少一个游戏,对应这个游戏URL
    //的确认前缀应该去掉不再包含当前游戏,这个确认前缀可能会对应多个之前游戏的URL,所以
    //需要使用当前确认前缀的其他游戏往后推,更新哈希前缀,至到当前确认前缀不在包含之前的游戏
    //每个字符串最多求一遍确认前缀,复杂度O(n*len)
    ans[n] = mp.size();
    for(int i=n;i>1;i--){
        for(int j = l[i];j<r[i];j++){
            if(j>l[i]) sum[i] = sum[i]*P+s[i][j];
            auto it = mp.find(sum[i]);
            if(it==mp.end()) break;//当前确认前缀不包含之前的游戏,退出
            for(auto k:it->second){
                if(k==i) continue;
                l[k]++;
                sum[k] = sum[k]*P+s[k][l[k]];
                mp[sum[k]].push_back(k);
            }
            mp.erase(it);
        }
        ans[i-1] = mp.size();
    }
    
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}

第九场

H题

  • 思路
    按照三进制模拟
  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;
template<typename T>
void Debug(T x,string s){

    cout<<s<<": "<<x<<endl;
}

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
int a[4]={0,2,3,6};
void solve()
{
	int n;cin>>n;
    vector<int> vec;
    while(n){
        int x=n%3;
        if(x==0) x=3;
        vec.push_back(a[x]);
        if(x==3){
            n/=3;n--;
        }
        else n/=3;
    }
    reverse(vec.begin(),vec.end());
    for(auto i:vec){
        cout<<i;
    }
    cout<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
    
	while (t--)
		solve();
}

E题

  预置知识

   主席树

### 主席树模板
#include<cstdio>
#include<cstring> 
#include<algorithm>

#define N 200005
#define lc(x) tree[x].lc
#define rc(x) tree[x].rc
#define size(x) tree[x].size

using namespace std;
int n,m,q,t;
int a[N],b[N],root[N];
struct node
{
	int lc,rc,size;
}tree[N*40];

void disc()
{
	int i;sort(b+1,b+n+1);
	m=unique(b+1,b+1+n)-(b+1);
	for(int i=1;i<=n;i++) {
		a[i]=lower_bound(b+1,b+1+m,a[i])-b;
	}
}

void build(int y,int &x,int l,int r,int k)
{
	x=++t;
	tree[x]=tree[y];
	size(x)++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(k<=mid) build(lc(y),lc(x),l,mid,k);
	else build(rc(y),rc(x),mid+1,r,k);
}

int query(int y,int x,int l,int r,int k)
{
	if(l==r) return l;
	int mid=(l+r)>>1,delta=size(lc(y))-size(lc(x));
	if(k<=delta) return query(lc(y),lc(x),l,mid,k);
	else return query(rc(y),rc(x),mid+1,r,k-delta);
}
int main()
{
	int k,l,r;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);b[i]=a[i];
	}
	disc();
	for(int i=1;i<=n;i++){
		build(root[i-1],root[i],1,m,a[i]);
	}
	for(int i=1;i<=q;i++){
		scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",b[query(root[r],root[l-1],1,m,k)]);
	}
}

   树上倍增


// dfs,用来为 lca 算法做准备。接受两个参数:dfs 起始节点和它的父亲节点。
void dfs(int root, int fno) {
  // 初始化:第 2^0 = 1 个祖先就是它的父亲节点,dep 也比父亲节点多 1。
  fa[root][0] = fno;
  dep[root] = dep[fa[root][0]] + 1;
  // 初始化:其他的祖先节点:第 2^i 的祖先节点是第 2^(i-1) 的祖先节点的第
  // 2^(i-1) 的祖先节点。
  for (int i = 1; i < 31; ++i) {
    fa[root][i] = fa[fa[root][i - 1]][i - 1];
    cost[root][i] = cost[fa[root][i - 1]][i - 1] + cost[root][i - 1];
  }
  // 遍历子节点来进行 dfs。
  int sz = v[root].size();
  for (int i = 0; i < sz; ++i) {
    if (v[root][i] == fno) continue;
    cost[v[root][i]][0] = w[root][i];
    dfs(v[root][i], root);
  }
}

// lca。用倍增算法算取 x 和 y 的 lca 节点。
int lca(int x, int y) {
  // 令 y 比 x 深。
  if (dep[x] > dep[y]) swap(x, y);
  // 令 y 和 x 在一个深度。
  int tmp = dep[y] - dep[x], ans = 0;
  for (int j = 0; tmp; ++j, tmp >>= 1)
    if (tmp & 1) ans += cost[y][j], y = fa[y][j];
  // 如果这个时候 y = x,那么 x,y 就都是它们自己的祖先。
  if (y == x) return ans;
  // 不然的话,找到第一个不是它们祖先的两个点。
  for (int j = 30; j >= 0 && y != x; --j) {
    if (fa[x][j] != fa[y][j]) {
      ans += cost[x][j] + cost[y][j];
      x = fa[x][j];
      y = fa[y][j];
    }
  }
  // 返回结果。
  ans += cost[x][0] + cost[y][0];
  return ans;
}

   树链剖分

void dfs1(int o) {
  son[o] = -1;
  siz[o] = 1;
  for (int j = h[o]; j; j = nxt[j])
    if (!dep[p[j]]) {
      dep[p[j]] = dep[o] + 1;
      fa[p[j]] = o;
      dfs1(p[j]);
      siz[o] += siz[p[j]];
      if (son[o] == -1 || siz[p[j]] > siz[son[o]]) son[o] = p[j];
    }
}
void dfs2(int o, int t) {
  top[o] = t;
  cnt++;
  dfn[o] = cnt;
  rnk[cnt] = o;
  if (son[o] == -1) return;
  dfs2(son[o], t);  // 优先对重儿子进行 DFS,可以保证同一条重链上的点 DFS 序连续
  for (int j = h[o]; j; j = nxt[j])
    if (p[j] != son[o] && p[j] != fa[o]) dfs2(p[j], p[j]);
}
  • 思路

要首先要用树上倍增处理出他的父亲,这样就可以首先找到最高能传染的结点,然后往下扫

  • 代码(树链剖分)
#include<bits/stdc++.h>
#define sc(a) scanf("%d",&a)
using namespace std;
typedef long long ll;
const int maxn= 4e5+5;
ll a[maxn],n;
vector<int> G[maxn];
int son[maxn],Size[maxn],fa[maxn][21],top[maxn],pos[maxn],cnt,dfn[maxn];
int Min[maxn],Max[maxn];
void dfs1(int u,int pre){
    Size[u] = 1;
    fa[u][0]=pre;
    for(int i=1;i<20;i++) fa[u][i] = fa[fa[u][i-1]][i-1];
    /*fa用来倍增出这个节点的父亲,父亲的父亲......*/ 
    for(int i=0;i<G[u].size();i++){
        int v = G[u][i];
        if(!Size[v]){
            dfs1(v,u);
            Size[u]+=Size[v];
            if(Size[son[u]]<Size[v]){
                son[u]=v;  //树链剖分,重儿子 
            }
        }
    }
}
void dfs2(int u,int tp){
    top[u]=tp;
    dfn[++cnt]=u;  //dfs序 
    pos[u]=cnt;  //表示这个节点在数组中的位置 
    if(son[u]) dfs2(son[u],tp);  //优先走重儿子 
    for(int i=0;i<G[u].size();i++){
        int v = G[u][i];
        if(!top[v]) dfs2(v,v);
    }
}
void pushup(int rt){
    Max[rt] = max(Max[rt<<1],Max[rt<<1|1]);
    Min[rt] = min(Min[rt<<1],Min[rt<<1|1]);
}
void build(int l,int r,int rt){
    if(l==r){
        Max[rt] = a[l];
        Min[rt] = a[l];
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}
int query(int L,int R,int l,int r,int rt,int t1,int t2){
    //L、R代表查询的区间,l、r表示当前所在区间,rt当前所在节点,t1是最高温度,t2是最低温度 
    int ans = 0;
    if(L<=l&&r<=R){   //如果当前所在节点在我们要查询的区间中 
        int m = (l+r)>>1;
        if(Min[rt]>=t2) {   //这个区间的最低温度高于最低温度就满足 
            ans+=r-l+1;    //返回这个区间的节点数 
            return ans;
        }
        if(Max[rt]<t2){   //如果这个区间的最高温比t2还小,就不满足,返回0; 
            return 0;
        }
    }    
    int m = (l+r)>>1;
    if(L<=m) ans+=query(L,R,l,m,rt<<1,t1,t2);
    if(R> m) ans+=query(L,R,m+1,r,rt<<1|1,t1,t2);
    return ans;
}
void solve(){
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int cnt = 0;
    dfs1(1,1),dfs2(1,1);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%lld",&x);
        a[pos[i]]= x;
    }
    build(1,n,1);
    int Q;
    cin>>Q;
    for(int i=1;i<=Q;i++){
        int x,t2,t1;
        scanf("%d%d%d",&x,&t2,&t1);
        if(a[pos[x]]<t2||a[pos[x]]>t1){
            cout<<0<<'\n';
        }
        else{
            for(int i=19;i>=0;i--){
                if(a[pos[fa[x][i]]]>t1) continue;
                x = fa[x][i];
            }
            int ans = query(pos[x],pos[x]+Size[x]-1,1,n,1,t1,t2);
            cout<<ans<<'\n';
        }
    }
    return ;
}
int main()
{
    int T=1;
    while(T--){
        solve();
    }
    return 0;
}

第八场

E题

  • 思路

枚举每一位的可能,然后乘起来就是答案

image-20210820113001978

  • 代码
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
using namespace std;

const int N=1e5+7;
ll b[N],c[N],d[N];
signed main()
{
    int n;scanf("%d",&n);
    for(int i=2;i<=n;i++) scanf("%lld",b+i);
    for(int i=2;i<=n;i++) scanf("%lld",c+i);
    for(int i=2;i<=n;i++) d[i]=c[i]-b[i];
    ll ans=1;
    for(int i=0;i<=31;i++){
        int bit0=1,bit1=1;
        for(int j=2;j<=n;j++){
            int nowbit0=0,nowbit1=0;
            int x=b[j]>>i&1;
            int y=d[j]>>i&1;
            if(!x&&!y) nowbit0=bit0;
            else if(x&&!y) nowbit1=bit0,nowbit0=bit1;
            else if(x&&y) nowbit1=bit1;
            bit1=nowbit1,bit0=nowbit0;
        }
        ans*=(bit1+bit0);
    }
    cout<<ans<<endl;
}

A题

  • 思路

签到题,注意取模

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=4933;
#define Debug(x) cout<<"x: "<<x<<endl;
#define PII pair<int,int>
// #define x first
// #define y second
#define PB push_back

struct node{
	int x,y,z;
}ab[N];
ll pow_mod(ll a, ll b, ll p){//a的b次方求余p
    ll ret = 1;
    while(b){
        if(b & 1) ret = (ret * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return ret;
}
ll Fermat(ll a, ll p){//费马求a关于b的逆元
    return pow_mod(a, p-2, p);
}
void solve()
{
	int n,m,k,a,l;
	cin>>n>>m>>k>>a>>l;
	ll ans=1;
	ll fz=1,fm=1;
	for(int i=1;i<=k;i++){
		cin>>ab[i].x>>ab[i].y>>ab[i].z;
		//ans=ans%mod*y%mod*(z,mod)%mod;
	}
	ll sum=0;
    
	for(int i=1;i<=k;i++){
		ll x=ab[i].x,y=ab[i].y,z=ab[i].z;//cin>> a[i].x >> a[i].y >> a[i].z;
		if(x==0){
            continue;
        }
        y=z-y;
		fz=(fz*y)%mod;
        
		fm=(fm*z)%mod;
	}
	ans=(fz%mod)*(Fermat(fm,mod))%mod;
	ans=(ans+a)%mod;
	cout<<ans<<endl;
}
int main()
{
	
#ifndef ONLINE_JUDGE
	freopen("b.txt", "r", stdin);
	freopen("bout.txt", "w", stdout);
#endif
	//cout<<Fermat(12,4933)<<endl;
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	while (t--)
		solve();
}

F题

  • 思路

第一种,第二种操作还是很好搞的,只要预处理出一个前缀和就行了;

第三种操作可以预先处理到终点是否可达,用 b i t s e t bitset bitset 优化。

  • 代码
#include<bits/stdc++.h> 
using namespace std;
const int N=510;
struct node{
	int rx,ly,ry,id;
};
vector<node> p[N],t[N];
char mapp[N][N];
int x[N][N],y[N][N],n,m,q;
bool ans[500010];
bitset<N> vis[N];

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf(" %c",&mapp[i][j]);
			x[i][j]=x[i][j-1]+(mapp[i][j]=='1');
			y[i][j]=y[i-1][j]+(mapp[i][j]=='1');	
		}
		
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		int o,x1,y1,x2,y2;
		scanf("%d%d%d%d%d",&o,&x1,&y1,&x2,&y2);
		if(o==1){
			if(y1==y2&&x2>=x1&&y[x2][y1]-y[x1-1][y1]==0) ans[i]=1;
		}
		else if(o==2){
			if(x1==x2&&y2>=y1&&x[x1][y2]-x[x1][y1-1]==0) ans[i]=1;
		}
		else if(x1<=x2&&y1<=y2)p[x1].push_back(node{x2,y1,y2,i});
	}
	for(int l=1;l<=n;l++){
	//将当前行上每个起点出发的询问,存储到终点的每行中 
		for(auto o:p[l])t[o.rx].push_back(o);
		//初始化 
		//vis[x][y] 表示 (l,y) 能否走到 (?,x)
		for(int i=1;i<=m;i++) vis[i].reset();
		for(int i=1;i<=m;i++){
			//若(l,i)有障碍物,清零 
			if(mapp[l][i]=='1') vis[i].reset();
			//(l,i)无障碍物,标记(l,i) 可以走到 (l,i) ,合并上能走到 (l,i-1) 的起点方案 
			else vis[i][i]=1,vis[i]|=vis[i-1];
		}
		//枚举行 
		for(int r=l;r<=n;r++){
			for(int i=1;i<=m;i++){
			//走到 (r,i) 的方案,要合并上能走到 (r,i-1) 的方案 
				vis[i]|=vis[i-1];
				//若 (r,i) 存在障碍物,则无方案 
				if(mapp[r][i]=='1')vis[i].reset();
			}
			for(auto o:t[r]){
				ans[o.id]=vis[o.ry][o.ly];
			}
			t[r].clear();
		}
	}	
	for(int i=1;i<=q;i++){
		if(ans[i])printf("yes\n");
		else printf("no\n");
	}
}

第七场

H题

  • 思路

暴力搞就行。

  • 代码
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N=1e6+10;
ll num[N];

void solve() {
    int n;
    scanf("%d",&n);
    ll max_=0;
    for (int i=1; i <= n; ++i) {
        ll x;
        scanf("%lld",&x);
        max_=max(max_,x);
        num[x]++;
    }
    ll ans=0*1ll;
//     while(1){
//         ll cnt=0;
//         cnt++;
//     }
    for (ll i=1; i<= max_; ++i) {
        if (!num[i]) continue;
        if (i*i <= 1e6) ans=ans+num[i]*num[i]*num[i*i];
        for (int j=i+1; i*j <= 1e6; ++j) {
            ans=ans+2*num[j]*num[i]*num[i*j];
        }
//         cout<<i<<" "<<ans<<endl;
    }
    printf("%lld\n",ans);
}


int main(){
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
    // int t;
    // scanf("%d",&t);
    // while (t--) 
        solve();
}

I题

  • 思路

注意特判 全 0 0 0 的情况。

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
void solve()
{
	long long x,s;
    cin>>x>>s;
    int cnt=0;
    for(int i=31;i>=0;i--){
        if(!((s>>i)&1)){
            if((x>>i)&1){
                cout<<0<<endl;return;
            }
        }
        else{
            if(((x>>i)&1)){
                cnt++;
            }
        }
    }
    ll ans=pow(2,cnt);
    if(x==s)
    cout<<ans-1<<endl;
    else cout<<ans<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	while (t--)
		solve();
}

F题

  • 思路

由题意可知,我们需要先根据第二棵树,得到判断两两节点是否构成祖孙的依据。直接用暴力的手段记录每个节点的祖先,时间复杂度达到了 $ O(n^2)$ ,一定会超时,故我们需要尽量去寻找时间复杂度为 O ( n ) O(n) O(n) 的方法。

这就需要我们掌握一个知识点:树的DFS序。我们从根节点开始DFS整棵树,在进栈处和出栈处设置数组,分别记录每一个节点进栈和出栈的顺序。然后我们利用桶的思想,得到每一个节点进出栈的时间(即进栈时间表是线程的前半段,出栈时间表是线程的后半段)。

我们可以得到如下两个结论:
1.如果两个节点构成祖孙,那么这两个节点同在一条树链上,且祖先节点比子孙节点先入栈,子孙节点比祖先节点先出栈。
2.如果两个节点不构成祖孙,那么这两个节点不在一条树链上,且其中一个节点相对另外一个节点既先进栈,又先出栈。

我们根据以上两个结论查表,比较两个节点的进出栈时间,即可判断两个节点是否构成祖孙。建表的时间复杂度为 O ( n ) O ( n ) O(n)

然后我们在第一棵树上进行搜索。由于要构造的最大子集中的节点在第一棵树上是相连的,还要满足在第二棵树上两两不构成祖孙的条件,并且最后要求的是子集可以容纳节点个数的最大值,容易想到利用树上滑动窗口维护这一子集。

由树的 D F S DFS DFS 序,每次搜索会先彻底搜索完整条树链,再向上回溯。那么滑动窗口则在整条树链上进行滑动,以满足在第一棵树上两两构成祖先的关系。用传统的双指针模拟窗口,在树上则显得不易操作。因此我们用双向队列来维护滑动窗口。在每次向滑动窗口中加入节点前,我们先将右指针指向队首,如果指向的节点与要新加的节点不构成祖孙,则右指针向左滑动,直到不满足条件为止。然后我们加入新节点,并更新答案,即滑动窗口当前的长度。

至于左指针,就是右指针向左移动到达的极限位置。在正向搜索完整条树链后,我们利用树的 $DFS $ 序,将队列中的节点从队尾弹出,队首树链的公共部分保留,并接着搜索该公共部分的其它树链分支。

我们用静态邻接表 v e c t o r vector vector 来保存树的边集。不要忘了将给出的有向边转换成无向边。具体的实现方式见代码。

  • 代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
vector<int> e1[maxn],e2[maxn];//树1,树2
int n,u,v,ans;
int inorder[maxn],outorder[maxn];//进栈序,出栈序
int incnt,outcnt;
int innode[maxn],outnode[maxn];//进栈节点,出栈节点
deque<int> q;//双向队列维护滑动窗口
void iodfs(int cur,int fa){//记录树2的DFS序
    innode[incnt++]=cur;//记录进栈节点
    for(auto nxt:e2[cur]){//访问当前节点的邻接节点
        if(nxt!=fa)iodfs(nxt,cur);//如果邻接节点不是父节点,继续DFS
    }
    outnode[outcnt++]=cur;//记录出栈节点
}
void get_ancestor(){//建立进出栈时间表
    iodfs(1,0);
    for(int i=0;i<=n-1;i++){//利用桶的思想统计进出栈的顺序
        inorder[innode[i]]=outorder[outnode[i]]=i;
    }
}
bool is_ancestor(int u,int v){//根据进出栈时间表判断两节点是否构成祖先
    if(inorder[u]<inorder[v]&&outorder[u]>outorder[v])return true;
    if(inorder[u]>inorder[v]&&outorder[u]<outorder[v])return true;
    if(inorder[u]<inorder[v]&&outorder[u]<outorder[v])return false;
    if(inorder[u]>inorder[v]&&outorder[u]>outorder[v])return false;
}
void dfs(int cur,int fa,int left){//DFS树1
    q.push_back(cur);//将当前节点加入队尾
    ans=max<int>(ans,q.size()-left);//更新答案
    for(auto nxt:e1[cur]){//访问当前节点的邻接节点
        if(nxt!=fa){//如果邻接节点不是父节点
            int right=q.size();//初始化右指针
            while(right>left){//尝试向左移动
                if(is_ancestor(q[right-1],nxt)==false) right--;//树2中不是祖先,就左移扩大滑动窗口长度
                else break;//否则终止
            }
            dfs(nxt,cur,right);//右指针左移到极限成为左指针
        }
    }
    q.pop_back();//回溯时从队尾弹出路径上的节点
}
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++){//释放容器的内存空间
            e1[i].clear();
            e2[i].clear();
        }
        for(int i=1;i<=n-1;i++){
            cin>>u>>v;
            e1[u].push_back(v);//有向边转无向边
            e1[v].push_back(u);
        }
        for(int i=1;i<=n-1;i++){
            cin>>u>>v;
            e2[u].push_back(v);//有向边转无向边
            e2[v].push_back(u);
        }
        q.clear();//释放队列的内存空间
        incnt=0,outcnt=0,ans=0;//初始化
        get_ancestor();//根据树2建表
        dfs(1,0,0);//根据树1搜索
        cout<<ans<<endl;
    }
    return 0;
}

第六场

I题

  • 思路

思维题,考虑全覆盖,把空着的取消掉。既得包含全部,又得不包含,所以前一个左端一直到末尾,再从末尾连到上一个右端

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
void solve()
{
	int n,m;cin>>n>>m;
    vector<PII> res;
    for(int i=1;i<=m;i++){
        int l,r;cin>>l>>r;
        if(l<=r){
            res.PB({l,r});
        }
        else{
            res.PB({l,n});
            res.PB({1,r});
        }
    }
    sort(res.begin(),res.end());
    vector<PII> ans;
    
    int l=res[0].x,r=res[0].y;
    for(int i=1;i<res.size();i++){
        if(res[i].x<=r){
            r=max(res[i].y,r);
        }
        else{
            ans.push_back({l,r});
            l=res[i].x,r=res[i].y;
        }
    }
    ans.push_back({l,r});
//     for(auto &item:ans){
//         cout<<item.x<<" "<<item.y<<endl;
//     }
    
    if(ans.size()>2000){
        cout<<-1<<endl;
    }
    else{
        cout<<ans.size()<<endl;
        cout<<ans[0].x<<" "<<ans[ans.size()-1].y<<endl;
        for(int i=0;i<ans.size()-1;i++){
            cout<<ans[i+1].x<<" "<<ans[i].y<<endl;
        }
    }

}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while (t--)
		solve();
}

H题

  • 思路
    关键是 s u m / t sum/t sum/t ,最多装这些或者这些里的最大值;
    然后直接模拟,注意指针可以不开那么多,找到递推关系。
  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<ll,ll>
#define x first
#define y second
#define PB push_back
// void solve()
// {
// 	int n,m;cin>>n>>m;
    
// }

PII a[N];

bool cmp(PII a,PII b)
{
    return a.x>b.x;
}
bool vis[N];
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;cin>>n>>m;
    ll sum=0;
    ll t=0;
    for(int i=1;i<=n;i++){
        cin>>a[i].x;t=max(t,a[i].x);a[i].y=i;sum+=a[i].x;
    }
    if(m==1){
        int last=0;
        for(int i=1;i<=n;i++){
            cout<<1<<" "<<1<<" ";
            cout<<last<<" "<<last+a[i].x<<endl;
            last+=a[i].x;
        }
        return 0;
    }
    ll tt=sum/m;
    if(sum%m){
        tt++;
    }
    
    t=max(t,tt);ll last=t;int p=1;
    for(int i=1;i<=n;i++){
        ll now=a[i].x;
        if(now<=last){
            cout<<1<<" "<<p<<" "<<t-last<<" "<<t-last+a[i].x<<endl;
            last-=now;
            if(last==0){
                last=t;p++;
            }
        }
        else{ 
            now-=last;
            cout<<2<<" "<<p+1<<" "<<0<<" "<<now<<" ";
           
            cout<<p<<" "<<t-last<<" "<<t<<endl; 
            p++;
            last=t-now;
        }
    }
    
}

H题

  • 思路
    %d之后,线段树扫描线处理
  • 代码
#include<bits/stdc++.h>

using namespace std;

const int N=100005;

#define PII pair<int,int> 
// #define x first 
// #define y second

vector<PII> op0[N],op1[N];
int n,D,t[N<<2],tg[N<<2];

void change(int k,int l,int r,int x,int y,int v)
{
    if(x<=l&&r<=y){
        t[k]+=v;tg[k]+=v;return;//懒惰标记
    }
    int mid=(l+r)>>1;
    if(x<=mid) change(k<<1,l,mid,x,y,v);
    if(y>mid) change(k<<1|1,mid+1,r,x,y,v);
    t[k]=min(t[k<<1],t[k<<1|1])+tg[k];//+tg[k]其实就是把懒惰标记下传//更新父结点信息。
}

int ask(int k,int l,int r)
{
    if(l==r) return l;
    int mid=(l+r)/2;
//因为要找到一个结点t[k]=0。t[k]!=0;
    if(tg[k]+t[k<<1]==t[k]){
        return ask(k<<1,l,mid);
    }
    return ask(k<<1|1,mid+1,r);
}

int main()
{
    scanf("%d%d",&n,&D);
    int del=(1<<30)/D*D;
    for(int i=1;i<=n;i++){
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        x1+=del,x2+=del,y1+=del,y2+=del;
        vector<PII> _x,_y;
        if(x2-x1>=D){
            _x.push_back({0,D-1});
        }
        else if((x2-1)%D>=x1%D){
            _x.push_back({x1%D,(x2-1)%D});
        }
        else{
            _x.push_back({x1%D,D-1});
            _x.push_back({0,(x2-1)%D});
        }
                //这里后一个减一是为了防止 1-5 5-8被覆盖了两次,改成1-4,5-7就不会了。
        if(y2-y1>=D){
                        //全部覆盖
            _y.push_back({0,D-1});
        }
        else if((y2-1)%D>=y1%D){
                        //一段覆盖
            _y.push_back({y1%D,(y2-1)%D});
        }
        else{
                        //两端覆盖
            _y.push_back({y1%D,D-1});
            _y.push_back({0,(y2-1)%D});
        }
        for(auto j:_x)
            for(auto k:_y){
                                //分开op0是入边,op1是出边
                op0[j.first].push_back(k);
                op1[j.second+1].push_back(k);//这里加1是因为你取模放的时候减了1。
            }
//这里处理其实x不太重要,重要的是y,不是说重要不重要,就是偏重。
    }
    for(int i=0;i<D;i++){
                //更新覆盖区域  
                //入边。
        for(auto j:op0[i]){
            change(1,0,D-1,j.first,j.second,1);
        }
        // 出边
        for(auto j:op1[i]){
            change(1,0,D-1,j.first,j.second,-1);
        }
                //    
        if(t[1]==0){
            puts("YES");
            cout<<i<<" "<<ask(1,0,D-1)<<endl;return 0;
        }
    }
    puts("NO");
}

第五场

K题

  • 思路
    使用两个单调队列维护,一个最大值就是单减栈,一个最小值就是单增栈。
  • 代码
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn=1e5+10;
int n,m,k;
int num[maxn];
int q1[maxn],q2[maxn];

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>num[i];
    while(m--){
        //q1维护最大值,q2维护最小值
        cin>>k;
        ll ans=0;
        int l1=1,r1=0,l2=1,r2=0,l=1;
        for(int i=1;i<=n;i++){
            while(l1<=r1&&num[q1[r1]]<=num[i]) r1--;
            q1[++r1]=i;
            while(l2<=r2&&num[q2[r2]]>=num[i]) r2--;
            q2[++r2]=i;
            //cout<<i<<":"<<endl;
            //cout<<q1[l1]<<" "<<q2[l2]<<endl;
            //cout<<q1[r1]<<" "<<q2[r2]<<endl;
            while(l<=i&&num[q1[l1]]-num[q2[l2]]>k){
                ans+=n-i+1;
                l++;
                while(l1<r1&&q1[l1]<l) l1++;
                while(l2<r2&&q2[l2]<l) l2++;
                
            }
        }
        cout<<ans<<endl;
    }
}

H题

  • 思路
    硬模拟其实
  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;
ll a[5][N];
#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
ll dp1[N],dp2[N];
void solve()
{
    int n,m;cin>>n>>m;
    for(int i=1;i<=n/2;i++){
        if(i&1){
            for(int j=1;j<=m;j++){
                if(j&1){
                    cout<<0;
                }
                else{
                    cout<<1;
                }
            }
            cout<<endl;
            for(int j=1;j<=m;j++){
                if(j&1){
                    cout<<0;
                }
                else{
                    cout<<1;
                }
            }
            cout<<endl;
        }
        else{
            for(int j=1;j<=m;j++){
                if(!(j&1)){
                    cout<<0;
                }
                else{
                    cout<<1;
                }
            }
            cout<<endl;
            for(int j=1;j<=m;j++){
                if(!(j&1)){
                    cout<<0;
                }
                else{
                    cout<<1;
                }
            }
            cout<<endl;
        }
    }
    if(!(n&1)) return;
    if(!((n/2)&1)){
        for(int j=1;j<=m;j++){
            if(j&1){
                cout<<0;
            }
            else{
                cout<<1;
            }
        }
        cout<<endl;
    }
    else{
        for(int j=1;j<=m;j++){
            if(!(j&1)){
                cout<<0;
            }
            else{
                cout<<1;
            }
        }
        cout<<endl;
    }
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.txt", "r", stdin);
	freopen("aout.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	while (t--)
		solve();
}

B题

  • 思路

image-20210821212313341

  • 代码
#include<bits/stdc++.h>

using namespace std;

const int N=1e5+10;

double w[N];

int main()
{
    int n;double cost;cin>>n>>cost;
    double pre=0.0,sum=0.0;
    for(int i=1;i<=n;i++){
        cin>>w[i];pre+=1.0*w[i];
    }
    
    sort(w+1,w+1+n);
    double ans=0.0;
    for(int i=1;i<n;i++){
        sum+=w[i]*1.0;
        ans+=(sum*1.0)/pow(2,n-i);
    }
    
    ans=min(ans+cost,pre);
    printf("%.7f\n",ans);
}

D题

  • 思路

image-20210821212753131

  • 代码
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

typedef  long long ll;
const int maxN = 2000005, mod = 1000000007;

ll inv[maxN + 1], f[maxN + 1];

string s1, s2;
long long dp[5005][5005];
int len1, len2;

void exgcd(int a, int b, ll &x, ll &y) //拓展欧几里得
{
    if(b == 0) {
        x = 1; y = 0;
        return ;
    }
    exgcd(b, a % b, y, x);
    y -= a / b * x;
}

void init()
{
    f[0] = 1;
    for(int i = 1; i <= maxN; ++i)
        f[i] = f[i - 1] * i % mod; //阶乘数组
    ll x, y;
    exgcd(f[maxN], mod, x, y);
    inv[maxN] = (x % mod + mod) % mod;
    for(int i = maxN - 1; i; --i) {
        inv[i] = inv[i + 1] * (i + 1) % mod; //逆元数组
    }
}

ll C(ll n, ll m)
{
    if(n == m || m == 0)
        return 1;
    if(m > n)
        return 0;
    return (f[n] * inv[m] % mod * inv[n - m] % mod) % mod;
}

int main()
{
    cin >> s1; cin >> s2;
    len1 = s1.length(); len2 = s2.length();
    s1 = " " + s1; s2 = " " + s2;
    init();
    for(int i = 1; i <= len1; ++i) {
        for(int j = 1; j <= len2; ++j) {
            if(s1[i] == s2[j])
                dp[i][j] = (dp[i - 1][j] + dp[i][j - 1] + 1) % mod;
            else
                dp[i][j] = ((dp[i - 1][j] + dp[i][j - 1]) % mod + mod - dp[i - 1][j - 1]) % mod;
        }
    }
    long long ans = 0;
    for(int i = 1; i <= len1; ++i) {
        for(int j = 1; j <= len2; ++j) {
            if(s1[i] < s2[j]) {
                long long n = len1 - i, m = len2 - j;
                ans = (ans + (dp[i - 1][j - 1] + 1ll) * C(1LL * n + m, 1LL * min(n, m)) % mod) % mod;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

第四场

J题

  • 思路

这个可以纵轴和横轴分开考虑,这样的话就会转化为分开求两个的最大平均值区间

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
// #define x first
// #define y second
#define PB push_back
int n,m,x,y;
double sum[N];
double An[N],Am[N];
double b[N];
double find(int n,double a[],int f)
{
    double l=-1e6, r=1e6;
    
    while(r-l>1e-10){
        double mid=(l+r)/2;
        for(int i=1;i<=n;i++){
            b[i]=a[i]-mid;
        }
        for(int i=1;i<=n;i++){
            sum[i]=sum[i-1]+b[i];
        }
        double MIN=(1<<31)-1, MAX=-1e10;
        for(int i=f;i<=n;i++){
            MIN=min(MIN, sum[i-f]); 
            MAX=max(MAX, sum[i]-MIN);
        }
        if(MAX>=0){
            l=mid;
        }
        else{
            r=mid;
        }
    }
    return r;
}

void solve()
{
	cin>>n>>m>>x>>y;
    for(int i=1;i<=n;i++) cin>>An[i];
    for(int i=1;i<=m;i++) cin>>Am[i];
    double an=find(n,An,x);//cout<<an<<endl;
    double am=find(m,Am,y);//cout<<am<<endl;
    double ans=an+am;
    cout<<fixed<<setprecision(10)<<ans<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;t=1;
	//cin >> t;
	while (t--)
		solve();
}

I题

  • 思路

因为你加1,顶多会消掉这样的“3 2”这样连续的。所以用总的逆序对减去这样的就行了

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;
int a[N];
int tree[N];
#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
int n;

int lowbit(int x)
{
    return x&(-x);
}

void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}

int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
void solve()
{
	cin>>n;
    map<int,int> mp;
    for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]=i;
    //memset(tree,0,sizeof(tree));
    int ans=0;
    int cnt_ans=0;
    for(int i=n;i;i--){
        ans+=sum(a[i]-1);
        add(a[i],1);
    }
    //cout<<ans<<endl;
    for(int i=n;i>1;i--){
        int l=mp[i],r=mp[i-1];
        //cout<<l<<" "<<r<<endl;
        if(l<r){
            i--;
            cnt_ans++;
        }
    }
    //cnt_ans++;
    //cout<<cnt_ans<<endl;
    cout<<ans-cnt_ans<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;t=1;
	//cin >> t;
	while (t--)
		solve();
}

F题

  • 思路

其实算个结论题,连通块中边的数量+点的数量正好是奇数,所以只要判断顶点数和边数相加的奇偶性就行了。

  • 代码
#include<bits/stdc++.h>

using namespace std;

int main()
{
    int n,m;cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y;cin>>x>>y;
        
    }
    if(!((n+m)%2)){
        cout<<"Bob"<<endl;
    }
    else{
        cout<<"Alice"<<endl;
    }
}

C题

  • 思路
    构造的话其实还是很好构造的,但是分类确实恶心了一点点

  • 代码

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;
int a,b,c,n;
#define PII pair<int,int>
// #define x first
// #define y second
#define PB push_back
struct node
{
    int cnt,p;string s="";
}A[6];

bool cmp1(node a,node b)
{
    return a.cnt<b.cnt;
}
bool cmp2(node a,node b)
{
    return a.p<b.p;
}

void print(string temp,int cnt1)
{
    for(int j=0;j<temp.size();j++){
        cout<<temp[j];
    }
       // cout<<temp.size()<<" "<<n<<endl;
    for(int j=temp.size();j<n;j++){
        cout<<(char('d'+cnt1));
    }cout<<endl;
}
void solve()
{
	
    for(int i=1;i<=3;i++){
        cin>>A[i].cnt;A[i].p=i;
    }
    cin>>n;
    a=A[1].cnt;b=A[2].cnt;c=A[3].cnt;
    sort(A+1,A+4,cmp1);
    if(A[2].cnt+A[3].cnt-A[1].cnt>n){
        cout<<"NO"<<endl;return;
    }
    for(int i=1;i<=A[1].cnt;i++){
        A[1].s+='a';A[2].s+='a';A[3].s+='a';
    }
    for(int i=1;i<=A[2].cnt-A[1].cnt;i++){
        A[2].s+='b';A[3].s+='b';
    }
    for(int i=1;i<=A[3].cnt-A[1].cnt;i++){
        A[1].s+='c';A[3].s+='c';
    }
    string s1=A[1].s,s2=A[2].s,s3=A[3].s;
//     print(s1,1);
//     print(s2,2);
//     print(s3,3);
//     sort(A+1,A+1+3,cmp2);
    if(a<=b&&b<=c){
        print(s1,1);
        print(s2,2);
        print(s3,3);
    }
    else if(a<=c&&c<=b){
        print(s2,1);
        print(s1,2);
        print(s3,3);
    }
    else if(b<=c&&c<=a){
        print(s3,1);
        print(s1,2);
        print(s2,3);
    }
    else if(b<=a&&a<=c){
        print(s3,1);
        print(s2,2);
        print(s1,3);
    }
    else if(c<=a&&a<=b){
        print(s2,1);
        print(s3,2);
        print(s1,3);
    }
    else if(c<=b&&b<=a){
        print(s1,1);
        print(s3,2);
        print(s2,3);
    }
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;t=1;
	//cin >> t;
	while (t--)
		solve();
}

E题

  • 思路

image-20210821215930118

  • 代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int maxN = 1e5 + 5;
int n, l[maxN], r[maxN], head[maxN], cnt, W[maxN], ans, sum;

struct SegmentTree {
    int val, laz;
    bool operator < (const SegmentTree &t)const {
        if(val != t.val)
            return val < t.val;
        return laz < t.laz;
    }
};

struct Edge {
    int from, to, w;
}e[maxN << 1];

inline void add(int u, int v, int w)
{
    e[++cnt].from = head[u];
    e[cnt].to = v;
    e[cnt].w = w;
    head[u] = cnt;
}

void dfs(int u,int fa,int val) { //第一步
    W[u]  =val;
    for(int i = head[u]; i; i = e[i].from) {
        int v =e [i].to,p =e [i].w;
        if(v != fa)
            dfs(v, u, val ^ p);
    }
}

vector<SegmentTree> V;
void operation(int l, int r, int val) //求出每个区间异或后的区间并作差分
{
    SegmentTree a1, a2;
    int len = r - l + 1;
    a1.val = (l ^ (val & (~(len - 1))));
    a2.val = a1.val + len;
    a1.laz = 1; a2.laz = -1;
    V.push_back(a1); V.push_back(a2);
}

void Search(int L,int R,int l,int r,int val) { //第二步:分区间
    if(L <= l && R >= r) {
        operation(l, r, val);
        return ;
    }
    int mid = (l + r) >> 1;
    if(L <= mid)
        Search(L, R, l, mid, val);
    if(R > mid)
        Search(L, R, mid + 1, r, val);
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d%d", &l[i], &r[i]);
    for(int i = 1; i < n; ++i) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        add(x, y, z); add(y, x, z);
    }
    dfs(1, 0, 0);
    for(int i = 1; i <= n; ++i)
        Search(l[i], r[i], 0, (1 << 30) - 1, W[i]);
    sort(V.begin(), V.end());//对差分数组进行差分
    int len = V.size(), sum = 0, ans = 0;
    for(int i = 0; i < len; ++i) { //求前缀和并做统计
        sum += V[i].laz;
        if(sum == n)
            ans += V[i + 1].val - V[i].val;
    }
    printf("%d\n", ans);
    return 0;
}

第三场

E题

  • 思路

oeis搞出来的,全球数学竞赛一道题目,挺难的。
搞出来后打表。

  • 代码
#include<bits/stdc++.h>
using namespace std;

long long a[10000005];
int main(){
    int T;
    long long n;
    long long N=1e18;
    scanf("%d",&T);
    int ln=0;
    for(long long i=2;1ll*i*i*i<=N;i++){
        long long x=i,y=i*i*i;
        while(y<=N&&y>=0){
            a[++ln]=y;
            if((N+x)/i/i<y) break;
            long long tmp=y*i*i-x;
            x=y;
            y=tmp;
        }
    }
    sort(a+1,a+ln+1);
    // for(int i=1;i<=100;i++) printf("%lld ",a[i]);
    while(T--){
        scanf("%lld",&n);
        int x=upper_bound(a+1,a+ln+1,n)-a;
        printf("%d\n",x);
    }
}

J题

  • 思路

从角去考虑,一个异色三角形必然会有两个角连接异色边

  • 代码
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
namespace GenHelper
{
    unsigned z1,z2,z3,z4,b,u;
    unsigned get()
    {
        b=((z1<<6)^z1)>>13;
        z1=((z1&4294967294U)<<18)^b;
        b=((z2<<2)^z2)>>27;
        z2=((z2&4294967288U)<<2)^b;
        b=((z3<<13)^z3)>>21;
        z3=((z3&4294967280U)<<7)^b;
        b=((z4<<3)^z4)>>12;
        z4=((z4&4294967168U)<<13)^b;
        return (z1^z2^z3^z4);
    }
    bool read() {
      while (!u) u = get();
      bool res = u & 1;
      u >>= 1; return res;
    }
    void srand(int x)
    {
        z1=x;
        z2=(~x)^0x233333333U;
        z3=x^0x1234598766U;
        z4=(~x)+51;
      	u = 0;
    }
}
using namespace GenHelper;
bool edge[8005][8005];

//bitset<8005> Bit1[8005],Bit0[8005],bit;
int main() {
    
#ifndef ONLINE_JUDGE
	freopen("a.txt", "r", stdin);
	freopen("aout.txt", "w", stdout);
#endif
    int n, seed;
    cin >> n >> seed;
    srand(seed);
    for (int i = 0; i < n; i++)
    	for (int j = i + 1; j < n; j++)
        	edge[j][i] = edge[i][j] = read();
    
    ll cnt1=0,cnt0=0;
    ll b=0,a=0;
    for(int i=0;i<n;i++){
        cnt1=0,cnt0=0;
        for(int j=0;j<n;j++){
            if(j==i) continue;
            if(edge[i][j]){
                cnt1++;
            }
            else{
                cnt0++;
            }
        }
        b+=(cnt1*(cnt1-1)/2+cnt0*(cnt0-1)/2);
        a+=(cnt1*cnt0);
    }
    ll ans=(b-a/2)/3;
    cout<<ans<<endl;
    return 0;
}

B题

  • 思路

关于为什么是最小生成树:

考虑如果 ( i , j ) (i,j) (i,j) 处被标记,就相当于将第 i i i 行和第 j j j 列关联到了一起。相互关联的某些行和列,根据规则就可以把它们的全部交点都标记。因此当且仅当所有的行列都相互关联的时候,可以将所有的点都标记。

最小生成树- Prim

image-20210822120008661

  • 代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef double db;
const int N=5005;
int cost[N][N];
int dis[2*N],vis[2*N];
ll n,m,a,b,c,d,p;
ll A;
int main(){
	cin>>n>>m>>a>>b>>c>>d>>p;
	A=a;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			A=(A*A*b+A*c+d)%p;
			cost[i][j]=A;
//			dd(i),dd(j),de(cost[i][j]);
		}
	}
	for(int i=1;i<=n+m;i++){
		dis[i]=1e9;
	}
	dis[1]=0;
	ll ans=0;
	for(int i=1;i<=n+m;i++){
		int mn=1e9,pos=0;
		for(int j=1;j<=n+m;j++){
			if(!vis[j]&&dis[j]<mn){
				mn=dis[j];
				pos=j;
			}
		}
		vis[pos]=1;
		ans+=dis[pos];
		if(pos<=n){
			for(int j=n+1;j<=n+m;j++){
				dis[j]=min(dis[j],cost[pos][j-n]);
			}
		}
		else if(pos>n){
			for(int j=1;j<=n;j++){
				dis[j]=min(dis[j],cost[j][pos-n]);
			}
		}
	} 
	printf("%lld",ans); 
	return 0;
}

第二场

C题

  • 思路

简单的找规律

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
void solve()
{
	
}
int main()
{
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;cin>>n>>m;
    if(n&1){
        if(m&1){
            cout<<"NO"<<endl;
           
        }
        else{
            cout<<"YES"<<endl;
        }
    }
    else{
        cout<<"YES"<<endl;
    }
}

K题

  • 思路

在数组 b b b 中,每个给出的 b i b_i bi 前一定有一段 1 , 2 , … , b i − 1 1, 2, \dots, b_i - 1 1,2,,bi1 的子序列,不妨把这段子序列连续地放在 b i b_i bi 前,之后判断构造出的数组 b b b 是否合法。

模拟就行,用个树状数组维护

  • 代码
#include<bits/stdc++.h>
using namespace std;

const int N=1e6+5;
int a[N],b[N];
bool vis[N];
int c[N];
int n;
int ask(int x)
{
	int ans=0;
	for(;x;x-=x&-x) ans+=c[x];
	return ans;
}

void add(int x,int y)
{
	for(;x<=n;x+=x&-x) c[x]+=y;
}

int query(int x)
{
	int l=1,r=n;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(ask(mid)<x) 
            l=mid+1; 
        else r=mid-1;
	}
	return l;
}

void solve()
{
    int k,ok=1;cin>>n>>k;
    for(int i=1;i<=n;i++){
        int id,x;cin>>id>>x;
        b[id]=x;
        if(b[id]>id){
            ok=0;
        }
    }
    if(!ok){
        cout<<"-1"<<'\n';return;
    }
    for(int i=1;i<=n;i++){
        add(i,1);
    }

    for(int i=1;i<=n;i++){
        if(!b[i]){
            b[i]=b[i-1]+1;
        }
        if(b[i]-b[i-1]>1){
            cout<<"-1"<<'\n';return;
        }
    }
    for(int i=n;i>=1;i--){
        a[i]=query(b[i]);
        add(a[i],-1);
    }

    for(int i=1;i<=n;i++){
        cout<<a[i]<<" \n"[i==n];
    }
}


int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    solve();
}

F题

  • 思路

那个轨迹是个球,所以就转化成了两个球求公共面积

  • 代码
#include <iostream>
#include <cmath>

using namespace std;

const long double pi = acos(-1);

typedef struct {
	long double x, y, z, r;
}Point;

Point s, R;

long double shi1(long double x1, long double x2, long double k)
{
	long double b=(x1-k*k*x2)/(1-k*k);return b;
}

long double CC(long double x1,long double x2,long double k)
{
	return( x1*x1-k*k*x2*x2)/(1-k*k);
}

long double dis(Point p, Point q) {
	long double ans = sqrt((p.x - q.x)*(p.x - q.x) + (p.y - q.y)*(p.y - q.y) + (p.z - q.z)*(p.z - q.z));
	return ans;
}


int main ()
{
    int t;
    cin >> t;
    while (t --)
    {
        long double Ax, Bx, Cx, Dx;
        long double Ay, By, Cy, Dy;
        long double Az, Bz, Cz, Dz;
        cin >> Ax >> Ay >> Az;
        cin >> Bx >> By >> Bz;
        cin >> Cx >> Cy >> Cz;
        cin >> Dx >> Dy >> Dz;
        long double k1, k2;
        cin >> k1 >> k2;

        long double O1x = shi1 (Ax, Bx, k1);
        long double O1y = shi1 (Ay, By, k1);
        long double O1z = shi1 (Az, Bz, k1);
        long double O2x = shi1 (Cx, Dx, k2);
        long double O2y = shi1 (Cy, Dy, k2);
        long double O2z = shi1 (Cz, Dz, k2);
        long double r1=sqrt(-(-O1x*O1x+CC(Ax,Bx,k1)-O1y*O1y+CC(Ay,By,k1)-O1z*O1z+CC(Az,Bz,k1)));
		long double r2=sqrt(-(-O2x*O2x+CC(Cx,Dx,k2)-O2y*O2y+CC(Cy,Dy,k2)-O2z*O2z+CC(Cz,Dz,k2)));

        R.x = O1x, R.y = O1y, R.z = O1z, R.r = r1;
        s.x = O2x, s.y = O2y, s.z = O2z, s.r = r2;
        if(s.r<R.r){
            swap(s,R);
        }
        long double ans = 0;
        long double d = dis(s, R);
			if (d >= s.r + R.r) {
				ans = 0;
			}
			else if (d + R.r <= s.r) {
				ans = (4.0 / 3)*pi*R.r*R.r*R.r;
                //cout<<1<<endl;
			}
			else {
				long double co = (s.r*s.r + d * d - R.r*R.r) / (2.0*d*s.r);
				long double h = s.r*(1 - co);
				ans = (1.0 / 3)*pi*(3.0*s.r - h)*h*h;
				co = (R.r*R.r + d * d - s.r*s.r) / (2.0*d*R.r);
				h = R.r*(1 - co);
                //cout<<2<<endl;
				ans += (1.0 / 3)*pi*(3.0*R.r - h)*h*h;
			}
       printf ("%.5Lf\n", ans);
    }
}

D题

  • 思路

模拟就行了

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
bool check(int a,int b)
{
    if(a==2&&b==8){
        return true;
    }
    else{
        return false;
    }
}
bool checkeq(int a,int b)
{
    if(a==b) return true;
    else return false;
}

int mod10(int a,int b){
	return (a+b)%10;
}
void solve()
{
	int a1,b1,a2,b2;cin>>a1>>b1>>a2>>b2;
    if(a1>b1) swap(a1,b1);
    if(a2>b2) swap(a2,b2);
    if(check(a1,b1)&&check(a2,b2)){
        cout<<"tie"<<endl;return;
    }
    else if(check(a1,b1)){
        cout<<"first"<<endl;return;
    }
    else if(check(a2,b2)){
        cout<<"second"<<endl;return;
    }

    if(checkeq(a1,b1)&&checkeq(a2,b2)&&a1==a2){
        cout<<"tie"<<endl;return;
    }
    else if(checkeq(a1,b1)&&!checkeq(a2,b2)){
        cout<<"first"<<endl;return;
    }
    else if(!checkeq(a1,b1)&&checkeq(a2,b2)){
        cout<<"second"<<endl;return;
    }

	if(checkeq(a1,b1)&&checkeq(a2,b2)){
        if(a1>a2)
		{cout<<"first"<<endl;return;}
		else
		{
			cout<<"second"<<endl;return;
		}
		
    }

	if(mod10(a1,b1)>mod10(a2,b2)){
		cout<<"first"<<endl;return;
	}
	else if(mod10(a1,b1)<mod10(a2,b2)){
		cout<<"second"<<endl;return;
	}
	else {
		if(b1>b2){
			cout<<"first"<<endl;return;
		}
		else if(b1<b2){
			cout<<"second"<<endl;return;
		}
		else{
			cout<<"tie"<<endl;return;
		}
	}
}
int main()
{
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;cin>>t;
    while(t--){
        solve();
    }
}

I题

  • 思路
    简单的 b f s bfs bfs

  • 代码

#include<bits/stdc++.h>

using namespace std;
char a[21][21],b[21][21];
int f[21][21][21][21];
int dp[21][21][21][21];
char c[4] = {'D','L','R','U'};
int dx[] = {1,0,0,-1};
int dy[] = {0,-1,1,0};
char an[1000];
struct node{
	int x1,y1,x2,y2;
}x[21][21][21][21];

queue<node> q;
void bfs()
{
	memset(dp,0x3f,sizeof(dp));
	dp[20][20][20][1] = 0;
	q.push({20,20,20,1});            
	while(!q.empty()){
		node u = q.front();
		q.pop();
		int k = dp[u.x1][u.y1][u.x2][u.y2];
		for(int i = 0 ; i < 4; i ++){
			int dx1 = u.x1 + dx[i];
			int dx2 = u.x2 + dx[i];
			int dy1 = u.y1 + dy[i];
			int dy2 = u.y2 - dy[i];
			if(a[dx1][dy1] != '.') dx1 = u.x1,dy1=u.y1;
			if(b[dx2][dy2] != '.') dx2 = u.x2,dy2=u.y2;
			if(dp[dx1][dy1][dx2][dy2]>k+1){
				dp[dx1][dy1][dx2][dy2] = k+1;
				f[dx1][dy1][dx2][dy2] = i;
				x[dx1][dy1][dx2][dy2] = {u.x1,u.y1,u.x2,u.y2};
				q.push({dx1,dy1,dx2,dy2}); 
			} 
		}
	}
}

int ans=0;
void solve(node u)
{
	int x1 = u.x1, y1 = u.y1, x2 = u.x2, y2 = u.y2;
	a[x1][y1] = 'A', b[x2][y2] = 'A';
	if(x1 == 20&&y1 == 20&&x2 == 20&&y2==1) return;
	solve(x[x1][y1][x2][y2]);
	an[++ans] = c[f[x1][y1][x2][y2]];
}

int main()
{
	for(int i=1;i<=20;i++)
		cin>>a[i]+1>>b[i]+1;
	bfs();
	solve({1,20,1,1});
	cout<<dp[1][20][1][1]<<endl;
	cout<<an+1<<endl;
	for(int i = 1;i <= 20;i ++){
		cout<<a[i]+1<<" "<<b[i]+1;
		if(i!=20) cout<<endl;
	} 
}

第一场

A题

  • 思路

SG函数递推

  • 代码
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
//const double PI=3.14159;
//const int INF=0x7fffffff;
//const int INF=0x3f3f3f3f;
//const ll mod=1e9+7;
const int maxn=5010;
bool flag[maxn][maxn];

void init() {
    for(int i=0; i <= 5000; ++i) {
        for (int j=0; j <= 5000; ++j) {
            if (!flag[i][j]) {
                for (int k=1; k+i <= 5000; ++k) 
                    for (int s=0; s*k+j <= 5000; ++s) 
                        flag[k+i][s*k+j]=1;
                for (int k=1; k+j <= 5000; ++k) 
                    for (int s=0; s*k+i <= 5000; ++s) 
                        flag[s*k+i][k+j]=1;
            }
        }
    }
}

void solve() {
    int n,m;
    scanf("%d%d",&n,&m);
    if (!flag[n][m]) printf("Bob\n");
    else printf("Alice\n");
}

int main() {
    init();
    int t;
    scanf("%d",&t);
    for (int i=1; i <= t; ++i) {
        solve();
    }
    return 0;
}

B题

  • 思路

初中题

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
void solve()
{
	
}
int main()
{
	double r,a,b,h;
	cin>>r>>a>>b>>h;
	if(r*2<b){
		cout<<"Drop"<<endl;
	}
	else{
        cout<<"Stuck"<<endl;
		double cha=(a-b)/2;
        //cout<<cha<<endl;
		double xiebian=sqrt(h*h+cha*cha);
        //cout<<h*h+cha*cha<<endl;
        //cout<<xiebian<<endl;
		double t=r*xiebian/h;
       double y1=h*(b/2)/cha;
        double y2=h*(t)/cha;
		cout<<fixed<<setprecision(10)<<y2-y1<<endl;
	}
}

D题

  • 思路

签到,简单模拟

  • 代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e3+10,mod=1e9+7;
int n,m;
char mp[N][N];
#define PII pair<int,int>
#define x first
#define y second
#define PB push_back

int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++){
        cin>>mp[i];
    }
    string s;cin>>s;
    int len=s.size();
    //cout<<len<<endl;
    int cnt=0;
    int ans=0;
    for(int i=0;i<n;i++){
        int llen=0;
        for(int j=0;j<n;j++){
            if(mp[i][j]=='1'){
                ans+=(max(0,llen-len+1));
                llen=0;
            }
            else{
                llen++;
            }
        }
        //cout<<llen<<endl;
        ans+=(max(0,llen-len+1));
    }
    cout<<ans<<endl;
}

F题

  • 思路

小于100的可以暴力,大于的必然可以。

  • 代码
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
//const double PI=3.14159;
//const int INF=0x7fffffff;
//const int INF=0x3f3f3f3f;
//const ll mod=1e9+7;
const int maxn=1010;
int vec[maxn];

void init() {
    vec[0]=0;
    for (int i=1; i <= 99; ++i) {
        vec[i]=vec[i-1];
        if (i%3 == 0) vec[i]++;
        else {
            if (i >= 10) {
                int x,y;
                x=i%10;
                y=i/10;
                if (x%3 == 0 || y%3 == 0) vec[i]++;
            }
        }
    }
}

void fun() {
    ll l,r;
    ll num_l,num_r;
    scanf("%lld%lld",&l,&r);
    if (l-1 >= 100) num_l=vec[99]+l-100;
    else num_l=vec[l-1];
    if (r >= 100) num_r=vec[99]+r-99;
    else num_r=vec[r];
    cout<<num_r-num_l<<endl;
}

int main() {
    init();
    int t;
    scanf("%d",&t);
    for (int i=1; i <= t; ++i) {
        fun();
    }
    return 0;
}

G题

  • 思路

最优解性质

考虑任意一个最优解,我们把交换后的数字重新放回原来的位置,
相当于为每一个元素分配了它在答案中的符号。比如 A={0, 3}, B = {1, 2},最优解符号分配
是 A={-0,+3}, B={-1,+2}。考察符合要求的解符号分配规则,其实只要满足 A, B 中正号
总和和负号总和相等,
而 A、B 各自的正负号可以不一样。
 注意:有可能出现正负号和实际绝对值相反的情况,
 但是如果交换这一对正负号,只会使得解变优,
 所以在题目求最优的前提下,正负号是可以随意分配的。假设我们能任意指定 k 来求最优解,
 相当于是把 A, B 合在一起排序,
 取最大的 n 个填正号,最小的 n 个填符号即可。

最少步数得到最优解

考虑每一对元素 A_i,B_i,若它们符号不同,则直接忽略这一对元素;否则,一对都是+的元素需要和一对都是-的元素进行交换才能尽快达到最优解。

结论:

   n>2时,恰好 k 步与至多 k 步是等价的
   当 n>2 时,A 中一定至少存在两个 + 号或两个 - 号,
   此时如果我们交换这两个符号对应的数,则并不会使得原问题的解变得更劣。
    n=2 需要特殊判断。

求最优对换解

   考虑对于 A_i 和 A_j,如果需要答案变优,则需要两个区间没有交,
   变优 2*[min(A_i,B_i) - max(A_j, B_j)]。
   将所有的 min(A_i, B_i) 和 max(A_i, B_i) 排序,依次取前 k 大相减取和即可。
  • 代码
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=5e5+10;
ll a[N],b[N],add[N],des[N];

void solve() {
    int n,k;
    ll ans=0;
    scanf("%d%d",&n,&k);
    for (int i=1; i <= n; ++i) scanf("%lld",&a[i]);
    for (int i=1; i <= n; ++i) scanf("%lld",&b[i]);

    if (n == 2) {
        if (k&1) swap(a[1],a[2]);
        printf("%lld",(abs(a[2]-b[2])+abs(a[1]-b[1])));
        return;
    }
    for (int i=1; i <= n; ++i) {
        ans+=abs(a[i]-b[i]);
        add[i]=max(a[i],b[i]);
        des[i]=min(a[i],b[i]);
    }
    sort(add+1,add+n+1);
    sort(des+1,des+n+1,greater<int>());
    for (int i=1; i <= min(n,k); ++i) 
        if (des[i] > add[i]) ans=ans+2*(des[i]-add[i]);
    printf("%lld\n",ans);
}


int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	// int t;
    // scanf("%d",&t);
	// while (t--)
    solve();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的码泰君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值