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;
}