目录
第十场
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题
- 思路
枚举每一位的可能,然后乘起来就是答案
- 代码
#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题
- 思路
- 代码
#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题
- 思路
- 代码
#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题
- 思路
- 代码
#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
- 代码
#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,…,bi−1 的子序列,不妨把这段子序列连续地放在 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();
}