layout: post
title: 2018-2019 ACM-ICPC Brazil Subregional Programming Contest
author: "luowentaoaa"
catalog: true
tags:
mathjax: true
- codeforces
B.Marbles (nim博弈)
题意
一个棋盘n个点,每次可以把一个棋子移动到(x-d,y) or (x,y-d) or (x-d,y-d) 把其中一个棋子移动到(0,0)的人就胜利
问先手能否必胜
思路
我非常喜欢的一题,一开始看到题面第一反应我操nim博弈煞笔题,然后傻逼一样得写了 判断到全部到(0,0)就赢的博弈,后面发现我想错了,我写的是相当于把全部石子取完的博弈,而这题只要把其中一堆石子取完就赢了,所以不正确,还是许老师厉害,把题意转换一下,题目开始是到(0,0)就赢,那什么情况下肯定输呢,那就是到(1,2) (2,1)这种死局的情况,每个人肯定都不想发生这种情况,于是我们就把题目转化成 把石子取到(1,2) (2,1)这种奇异态,而如果一个人面临这种局面那就是稳输的,于是我们把(1,2)这种状态构造成取完,而后面一个人面临一个取完(只有必败态的)时候那他就必输,所以这题就变成把N堆石子取成必输态的NIM博弈了,注意要特判先手直接赢的情况
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e6+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
int sg[110][110];
int flag[500];
void init(){
for(int i=1;i<=100;i++)sg[i][0]=sg[0][i]=sg[i][i]=0;
for(int i=1;i<=100;i++){
for(int j=1;j<=100;j++){
if(i==j)continue;
memset(flag,0,sizeof(flag));
for(int k=1;k<i;k++)if(k!=j)flag[sg[k][j]]=1;
for(int k=1;k<j;k++)if(k!=i)flag[sg[i][k]]=1;
for(int k=1;k<min(i,j);k++)
if(i-k!=j-k)flag[sg[i-k][j-k]]=1;
for(int k=0;k<500;k++){
if(flag[k]==0){sg[i][j]=k;break;}
}
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n;
cin>>n;
init();
int f=0;
int ans=0;
while(n--){
int a,b;
cin>>a>>b;
ans^=sg[a][b];
if(a==b)f=1;
}
if(f||ans)cout<<"Y"<<endl;
else cout<<"N"<<endl;
return 0;
}
D.Unraveling Monty Hall(水题)
判断不是1的个数就行,大早上读题读得我怀疑人生
int n;
cin>>n;
int ans=0;
while(n--){
int a;cin>>a;ans+=(a==1)?0:1;
}
cout<<ans<<endl;
return 0;
}
E.Enigma (模拟)
题意
给你两个字符串s,t让判断前strlen(t)中的s有多少个是T中没有的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e2+50;
const ll inf=1e10;
const double eps=1e-5;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
string s;
string t;
cin>>s>>t;int ans=0;
int len=s.size();int len1=t.size();
for(int i=0;i<len-len1+1;i++){
bool flag=1;
for(int j=0;j<len1;j++)if(t[j]==s[i+j])flag=0;
ans+=flag;
}
cout<<ans<<endl;
return 0;
}
F.Music Festival (线段树/线段数组) (待补)
题意
有N个舞台(N<10),第i个舞台有Mi首歌(M总和<1000),给出每首歌的起始时间s,和终止时间t,以及价值val; 我们听完一首歌可以任意瞬移到另外的舞台,即不考虑时间边界。现在让你选择一种方案,使得每个舞台都至少听了一首歌,求最大价值。
题解
G.Gasoline (二分网络流)
题意
有p个加油站,r个油田,给出每个加油站需要的油以及每个油田库存的油,有C条加油站到油田的带权路,求每个加油站都能加满油时的最长的运输时间的最小值。
题解
二分最小值,建图,套网络流模板完事
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e5+50;
const ll inf=1e10;
const ll INF = 1000000000;
const double eps=1e-5;
struct Edge {
int from, to;
ll cap, flow;
};
struct Dinic {
int n, m, s, t;
vector<Edge> edges; // 边数的两倍
vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[maxn]; // BFS使用
int d[maxn]; // 从起点到i的距离
int cur[maxn]; // 当前弧指针
void ClearAll(int n) {
for(int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void ClearFlow() {
for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
}
void AddEdge(int from, int to, ll cap) {
edges.push_back((Edge){from, to, cap, 0});
edges.push_back((Edge){to, from, 0, 0});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS() {
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(s);
vis[s] = 1;
d[s] = 0;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i = 0; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(!vis[e.to] && e.cap > e.flow) {
vis[e.to] = 1;
d[e.to] = d[x] + 1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x, ll a) {
if(x == t || a == 0) return a;
int flow = 0, f;
for(int& i = cur[x]; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
e.flow += f;
edges[G[x][i]^1].flow -= f;
flow += f;
a -= f;
if(a == 0) break;
}
}
return flow;
}
ll Maxflow(int s, int t) {
this->s = s; this->t = t;
int flow = 0;
while(BFS()) {
memset(cur, 0, sizeof(cur));
flow += DFS(s, INF);
}
return flow;
}
vector<int> Mincut() { // call this after maxflow
vector<int> ans;
for(int i = 0; i < edges.size(); i++) {
Edge& e = edges[i];
if(vis[e.from] && !vis[e.to] && e.cap > 0) ans.push_back(i);
}
return ans;
}
void Reduce() {
for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
}
};
Dinic g;
int a[maxn],b[maxn],u[maxn],v[maxn],w[maxn];
int p,r,c,sum=0;
int S,T;
bool isok(int x){
g.ClearAll(T+1);
for(int i=1;i<=p;i++)g.AddEdge(S,i,a[i]);
for(int i=1;i<=r;i++)g.AddEdge(p+i,T,b[i]);
for(int i=1;i<=c;i++)if(w[i]<=x)g.AddEdge(u[i],p+v[i],inf);
ll res=g.Maxflow(S,T);
if(res==sum)return true;
else return false;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
cin>>p>>r>>c;
S=0,T=p+r+1;
for(int i=1;i<=p;i++)cin>>a[i],sum+=a[i];
for(int i=1;i<=r;i++)cin>>b[i];
for(int i=1;i<=c;i++)cin>>u[i]>>v[i]>>w[i];
int L=0,R=1e7,ans=-1;
while(L<=R){
int mid=(L+R)/2;
if(isok(mid))R=mid-1,ans=mid;
else L=mid+1;
}
if(ans==-1)cout<<-1<<endl;
else cout<<ans<<endl;
return 0;
}
I.Switches (模拟)
题意
给定N个灯,以及开始状态。M个开关集合,每次使用一个开关,这个集合的状态会改变。 现在一次按1到M个开关,即1,2,3,...M ,1, 2,... 问第几次会全部关掉。
思路
直接模拟咯,用bitset表示灯的状态
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e5+50;
const ll inf=1e10;
const ll INF = 1000000000;
const double eps=1e-5;
bitset<1010>b[1100],c;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n,m,k,x;
cin>>n>>m>>k;
for(int i=0;i<k;i++)cin>>x,c.set(x);
for(int i=1;i<=n;i++){
cin>>k;while(k--)cin>>x,b[i].set(x);
}
for(int i=1;i<=n;i++){
c^=b[i];
if(c.count()==0){
cout<<i<<endl;return 0;
}
}
for(int i=1;i<=n;i++){
c^=b[i];
if(c.count()==0){
cout<<i+n<<endl;
return 0;
}
}
cout<<-1<<endl;
return 0;
}
L.Subway Lines (树剖) (待补)
题意
给一棵树,询问两条路径上公共点的个数。