西北大学2019年春季校赛题解(G-L)

G. 房间迷宫

裸的最短路,spfa和dij都行。因为每个数字的因子不会很多。

话说现场赛过的人好少,都是RE什么的奇怪错误,看代码也是最短路,不知道什么锅QAQ,就算你过了吧。。因为这个题赛前预估算一道easy题。。

因子筛的话我是nlogn的,有些人读入一个筛一个可能会慢。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int maxn = 2e5 + 5;
const ll INF = 1e18;
struct node {
	ll x,d;
	node() {}
	node(ll a,ll b) {
		x=a;
		d=b;
	}
	bool operator < (const node & a) const {
		if(d == a.d) return x<a.x;
		else return d > a.d;
	}
};
vector<node> eg[maxn];
ll dis[maxn], n, a[maxn], b[maxn], c[maxn], d[maxn];;
vector<int>ff[maxn];

int main() {
	for(int i = 1;i <= (int)2e5; i++)
		for(int j = i;j <= (int)2e5; j += i)
			ff[j].pb(i);
	cin >> n;
	for(int i = 1; i <= n; i++)
		scanf("%d%d%d%d",a+i, b+i, c+i, d+i);
	for(int i = 1; i <= n; i++){
		eg[i].pb(node(d[i],a[d[i]]));
		for(int j = 0;j < ff[c[i]].size(); j++)
			if(i + ff[c[i]][j] <= n)
				eg[i].pb(node(i + ff[c[i]][j], a[i + ff[c[i]][j]] + b[i]));
	}
	
	for(int i = 0; i <= n; i++) dis[i] = INF;
	dis[1] = 0;
	priority_queue<node> q;
	q.push(node(1,dis[1]));
	while(!q.empty()) {
		node x = q.top();
		q.pop();
		for(int i = 0; i < eg[x.x].size(); i++) {
			node y = eg[x.x][i];
			if(dis[y.x] > x.d + y.d) {
				dis[y.x] = x.d + y.d;
				q.push(node(y.x, dis[y.x]));
			}
		}
	}
	printf("%lld\n",dis[n] + a[1]);
}

 

H.算法入门

直接输出Yes即可,在这里给出证明和构造题的做法。即给n个数,选出一些数能够被m整除,并输出这些数字。

我们会发现一个整数求余m的结果只会有m种,分别是0~m-1 。 然后对题目给的n个数搞一个前缀和,每个前缀和对m求余一次。总共有n个前缀和,即对m求余n次,会得到n个结果。由于n>=m,所以一定有一种结果出现了两次。假设1~i的前缀和求余m等于k,1~j的前缀和求余m也等于k,那么i~j区间的数字的和求余m就等于0.

如果n=m,那有可能每种求余结果都出现了一次啊!即0~m-1各出现1次,那么假设1~i的前缀和求余m等于0,那直接选出1~i这一段就好啦~~~

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;

int co[maxn],ww[maxn];

int main(){
	int n, m, q;
	long long sum = 0;
	cin >> n >> m;
	co[0] = 1;
	for(int i = 1; i <= n; i++){
		cin >> ww[i];
		sum += ww[i];
		if(!co[sum%m])	co[sum%m] = i;
		else{
			cout << i - co[sum%m] + 1 << endl;
			for(int j = co[sum%m] + 1; j <= i; j++)
				cout << ww[j] << " ";
			return 0;
		}
	}
}

I 多米诺骨牌

贪心,将成功概率最低的放在最后,然后线性递推。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fi first
#define se second
#define pii pair<int,int>
#define rep(i,a,n) for (int i=a;i<=n;i++)
const int maxn=2e5+5;
const ll mod=1e9 + 7;

ll pow_mod(ll a,ll b,ll c=mod,ll ans=1){while(b){if(b&1) ans=(a*ans)%c;a=(a*a)%c,b>>=1;}return ans;}
ll inv_mod(ll a){return pow_mod(a,mod-2);}
pair<int,int> p[maxn];
bool cmp(pii a,pii b){
	return a.fi * b.se > a.se * b.fi;
}
vector <int> ff[maxn];
int main(){
	ll n ,m, a, b, last = 0, now = 0;
	cin >> n;
	rep(i,1,n)
		cin >> p[i].fi >> p[i].se;
	sort(p+1,p+1+n,cmp);
	rep(i,1,n){
		now = ((last + 1) * p[i].se % mod * inv_mod(p[i].fi)) % mod;
		last = now;
	}	
	cout << now;
}

 

J NE的挑战Ⅲ

bfs序上的线段树

至于修改,推一下可得是去掉某个数的lowbit,任意一个数都可以在log次内被修改成0,所以标记可以暴力下推。

#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int maxn=1e6+10,maxm=2e6+10;

int casn,n,m,k;
vector<int> g[maxn];
int fa[maxn],val[maxn],dfn[maxn],l[maxn],r[maxn],mp[maxn],cnt;
void bfs(){
  queue<pii> que;
  que.push(pii(1,0));
  mp[++cnt]=1;
  dfn[1]=cnt;
  while(!que.empty()){
    auto now=que.front();que.pop();
    l[now.se]=min(l[now.se],dfn[now.fi]);
    r[now.se]=max(r[now.se],dfn[now.fi]);
    fa[now.fi]=now.se;
    for(auto i:g[now.fi]){
      if(i==now.se) continue;
      dfn[i]=++cnt;mp[cnt]=i;
      que.push(pii(i,now.fi));
      r[now.fi]=l[now.fi]=cnt;
    }
  }
}
class segtree{public:
#define nd node[now]
#define ndl node[now<<1]
#define ndr node[now<<1|1]
  struct segnode {
    int l,r;ll sum,tag;
    int mid(){return (r+l)>>1;}
    void update1(){
      tag&=tag-1;
      sum=tag*(r-l+1ll);
    }
    void update2(ll x){
      tag=x;
      sum=(r-l+1ll)*x;
    }
  };
  vector<segnode> node;
  segtree(int n) {node.clear();node.resize(n<<2|3);maketree(1,n);}
  void pushup(int now){
    nd.sum=ndl.sum+ndr.sum;
    if(ndl.tag==ndr.tag) nd.tag=ndl.tag;
    else nd.tag=-1;
  }
  void pushdown(int now){
    if(nd.tag!=-1){
      ndl.update2(nd.tag);ndr.update2(nd.tag);
    }
  }
  void maketree(int s,int t,int now=1){
    nd={s,t,0,0};
    if(s==t){
      nd.sum=nd.tag=val[mp[s]];
      return ;
    }
    maketree(s,nd.mid(),now<<1);
    maketree(nd.mid()+1,t,now<<1|1);
    pushup(now);
  }
  void update1(int s,int t,int now=1){
    if(s>nd.r||t<nd.l) return ;
    if(nd.tag!=-1&&s<=nd.l&&t>=nd.r){nd.update1();return ;}
    pushdown(now);
    update1(s,t,now<<1); update1(s,t,now<<1|1);
    pushup(now);
  }
  void update2(int s,int t,int x,int now=1){
    if(s>nd.r||t<nd.l) return ;
    if(s<=nd.l&&t>=nd.r){nd.update2(x);return ;}
    pushdown(now);
    update2(s,t,x,now<<1); update2(s,t,x,now<<1|1);
    pushup(now);
  }
  ll query(int s,int t,int now=1){
    if(s>nd.r||t<nd.l) return 0;
    if(s<=nd.l&&t>=nd.r) return nd.sum;
    pushdown(now);
    return query(s,t,now<<1)+query(s,t,now<<1|1);
  }
};
int main(){
  cin>>n>>m;
  rep(i,1,n) {
    g[i].clear();
    cin>>val[i];
  }
  rep(i,1,n-1){
    int a,b;cin>>a>>b;
    g[a].push_back(b);
    g[b].push_back(a);
  }
  bfs();
  segtree tree(n);
  while(m--){
    int a,b,c;
    cin>>a;
    casn++;
    if(a==1){
      cin>>b;
      tree.update1(l[b],r[b]);
      tree.update1(dfn[fa[b]],dfn[fa[b]]);
      tree.update1(dfn[b],dfn[b]);
    }else if(a==2){
      cin>>b>>c;
      tree.update2(l[b],r[b],c);
      tree.update2(dfn[fa[b]],dfn[fa[b]],c);
      tree.update2(dfn[b],dfn[b],c);
    }else {
      cin>>b;
      ll sum=tree.query(l[b],r[b])+tree.query(dfn[b],dfn[b]);
      if(b!=1) sum+=tree.query(dfn[fa[b]],dfn[fa[b]]);
      cout<<sum<<endl;
    }
  }
}

 

K 小小马

棋盘格子数为奇数时先手必胜(下中间的点)

偶数时后手必胜(和对方下中心对称的点)

 

L FF过马路

先离散化,处理好每个车道能否通行,然后枚举车道,递推下一个时间点可以去的道路,差分修改即可。

注意

2000 1

1 1 1000000000

的情况,NWPU的哥哥好像就这个点错了QAQ

应该输出1000002000

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;

#define fi first
#define se second
#define mp make_pair

int l[maxn], r[maxn];
pair<int, pair<int,int> > p[2 * maxn];
bool ok[maxn];
int dp[2 * maxn][maxn];

int main(){
	int n, m, st, ed, id, time;
	cin >> n >> m;
	for(int i = 1; i <= m; i++){
		cin >> id >> st >> ed;
		p[i * 2 - 1] = mp(st, mp(id, 0));//0代表这个车道有车,不能走 
		p[i * 2] = mp(ed, mp(id, 1));//1代表这个车道无车,可以走 
	}
	sort(p + 1, p + 2 * m + 1);//对时间点排序 
	memset(ok, true, sizeof ok);
	
	p[0] = mp(0, mp(0, 1));
	dp[0][0] = 1;dp[0][1] = -1;
	p[2 * m + 1] = mp((int)1e9+5000,mp(0, 1));
	
	for(int i = 0;i <= 2 * m; i++){//枚举时间点 
		for(int j = 1; j <= n; j++)//枚举当前在哪个车道 
			dp[i][j] += dp[i][j-1];//差分数组修改 
		if(dp[i][n]){//如果第n车道可达,输出答案,return 0 
			for(int j = n; j >= 0; j--)
				if(dp[i - 1][j])
					return cout << p[i - 1].fi + n - j,0;
		}
		ok[p[i].se.fi] = p[i].se.se;//更新车道是否有车的情况 
		int now = 0;
		for(int j = 0; j <= n; j++)
			if(ok[j])	l[j] = now;
			else	now = j + 1;
		now = n;
		for(int j = n; j >= 0; j--)
			if(ok[j])	r[j] = now;
			else	now = j - 1;//预处理出这个车道最远可达到的位置 
		for(int j = 0;j <= n; j++){
			if(!dp[i][j] || !ok[j])	continue;//如果此状态不存在,continue 
			dp[i + 1][min(j + p[i + 1].fi - p[i].fi, r[j]) + 1] -= 1;
			dp[i + 1][max(j - p[i + 1].fi + p[i].fi, l[j])]++;//差分数组修改下一时间点能到达的车道 
		}
	}
	for(int j = n; j >= 0; j--)
		if(dp[2 * m][j])
			return cout << p[2 * m].fi + n - j,0;
}

 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值