update PE707和这场的E很类似
Problem A:
solver dc.fang
因为距离不超过100考虑将距离作为dp的维度
#include <iostream>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <set>
#include <set>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <ctime>
#include <cmath>
#include <sstream>
#include <cstdlib>
#include <iomanip>
#include <list>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxt = 11000;
const int maxn = 5e3 + 10;
const int inf = 0x3f3f3f3f;
const int maxe = 4e6 + 10;
struct Point {
int x, y;
vector<pii> e;
int to(Point& b) {
return ceil(sqrt(1.0*(x-b.x)*(x-b.x)+1.0*(y-b.y)*(y-b.y)));
}
}o[maxn];
struct Edge {
int v, w, c, nxt;
}edges[maxe];
int cnt = 0, head[maxn];
void add_edge(int u, int v, int w, int c) { // w 长度 c 费用
edges[cnt] = {v, w, c, head[u]};
head[u] = cnt++;
edges[cnt] = {u, w, c, head[v]};
head[v] = cnt++;
}
int B, C[maxt], T, N;
int ans = inf;
struct node {
int i, c, w, pre;
};
int dp[1010][110];
void bfs(int s, int d)
{
memset(dp, inf, sizeof(dp));
queue<node> q;
q.push(node{s, 0, 0, -1});
while (!q.empty()) {
auto it = q.front();
int u = it.i, t = it.c, dis = it.w, fa = it.pre;
q.pop();
for (int i = head[u]; i != -1; i = edges[i].nxt) {
int v = edges[i].v, w = edges[i].w, c = edges[i].c;
if(fa == v) continue;
if(dis + w <= B) {
// cout << u << "->" << v << " : " << t + c << endl;
if(v == d) {
ans = min(ans, t + c);
continue;
}
if(t+c < dp[v][dis+w]) {
dp[v][dis+w] = t+c;
q.push(node{v, t + c, dis + w, u});
}
}
}
}
}
int main() {
ios::sync_with_stdio(false);
Point s, d;
cin >> s.x >> s.y >> d.x >> d.y;
cin >> B >> C[0];
cin >> T;
for (int i = 1; i <= T; i++) cin >> C[i];
cin >> N;
memset(head, -1, sizeof(head));
for (int i = 0; i < N; i++) {
cin >> o[i].x >> o[i].y;
int l; cin >> l;
for (int j = 0; j < l; j++) {
int t, m; cin >> t >> m;
o[i].e.push_back(pii(t, m));
}
}
for (int i = 0; i < N; i++) {
for (auto& j : o[i].e) {
int r = o[i].to(o[j.first]); // 长度
j.second = C[j.second] * r; // 费用
add_edge(i, j.first, r ,j.second);
}
}
o[N] = s; o[N+1] = d;
for (int i = 0; i < N; i++) {
int r1 = o[i].to(o[N]);
add_edge(N, i, r1, C[0] * r1);
int r2 = o[i].to(o[N+1]);
add_edge(N+1, i, r2, C[0] * r2);
}
add_edge(N, N+1, o[N].to(o[N+1]), o[N].to(o[N+1]) * C[0]);
bfs(N, N+1);
if(ans != inf) cout << ans << endl;
else cout << -1 << endl;
}
Problem B:
solver UCPRER
水题
Problem C:
solver Hugin
水题
Problem D:
模拟题,主要考虑一下怎么实现加括号和去括号。原始序列不需要很长因为每步操作最多只能减少一个元素。
#include<stack>
#include<cstdio>
#include<cstring>
#include<map>
#include<iostream>
#include<cstdio>
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>pii;
const int N=1e5+20;
pii unpair[N*2];
map<pii,int>ID;
stack<int> solve(string& s){
stack<int>stk;
static int id=N;
for(int i=0;i<N;i++){
stk.push(i);
}
function<bool(char)>work=[&](char op){
if(op=='C'){
stk.push(stk.top());
return 1;
}
if(op=='D'){
stk.pop();
return 1;
}
if(op=='U'){
int cur=stk.top();
if(cur<N)
return 0;
stk.pop();
pii& a=unpair[cur];
stk.push(a.second);
stk.push(a.first);
return 1;
}
if(op=='P'){
int a=stk.top();
stk.pop();
int b=stk.top();
stk.pop();
if(ID.count({a,b})){
stk.push(ID[{a,b}]);
}
else {
stk.push(ID[{a,b}]=id);
unpair[id++]={a,b};
}
return 1;
}
if(op=='S'){
int a=stk.top();
stk.pop();
int b=stk.top();
stk.pop();
stk.push(a);
stk.push(b);
return 1;
}
if(op=='L'){
if(work('U')&&work('S')&&work('D')){
return 1;
}
return 0;
}
if(op=='R'){
if(work('U')&&work('D')){
return 1;
}
return 0;
}
};
for(char ch:s){
if(!work(ch)){
stack<int>t;
return t;
}
}
return stk;
}
int main(){
string a,b;
cin>>a>>b;
stack<int>s=solve(a);
stack<int>t=solve(b);
if(s.size()!=t.size()){
cout<<"False";
return 0;
}
bool f=1;
while(!s.empty()){
if(s.top()!=t.top()){
f=0;
break;
}
s.pop();
t.pop();
}
cout<<(f?"True":"False");
}
Problem E:
solver Hugin
注意到确定了一行或一列就能知道整个矩阵的值了,那么将一行或一列作为变量高斯消元。
复杂度O(min(C,R)^3/64+min(C,R)CR/64)
#include<bits/stdc++.h>
using namespace std;
const int N=300;
bitset<N>qu[N];
bool gauss(bitset<N>*a,int n){
bitset<N>f;
f[n]=1;
for(int i=0;i<n;i++){
int p=-1;
for(int j=i;j<n;j++){
if(a[j][i]){
p=j;
break;
}
}
if(p!=-1){
swap(a[i],a[p]);
for(int j=0;j<n;j++){
if(j==i)continue;
if(a[j][i]){
a[j]^=a[i];
}
}
}
}
for(int i=0;i<n;i++){
if(a[i]==f)
return 0;
}
return 1;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
getchar();
vector<vector<char>>a(n);
for(int i=0;i<n;i++){
a[i]=vector<char>(m);
for(int j=0;j<m;j++){
a[i][j]=getchar();
getchar();
}
}
bool f=0;
if(n<m){
f=1;
vector<vector<char>>b(m);
for(int i=0;i<m;i++){
b[i]=vector<char>(n);
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
b[j][i]=a[i][j];
}
}
swap(n,m);
swap(a,b);
}
//n<m
vector<vector<bitset<N>>>ma(n);
for(int i=0;i<n;i++){
ma[i]=vector<bitset<N>>(m);
}
for(int i=0;i<m;i++){
ma[0][i][i]=1;//first row
}
for(int i=1;i<n;i++){
//bitsize m
for(int j=0;j<m;j++){
bitset<N>&b=ma[i][j];
b=ma[i-1][j];
if(i-2>=0)
b^=ma[i-2][j];
if(j-1>=0){
b^=ma[i-1][j-1];
}
if(j+1<m){
b^=ma[i-1][j+1];
}
if(a[i-1][j]=='B'){
b[m]=b[m]^1;
}
}
}
for(int i=0;i<m;i++){
bitset<N>&b=qu[i];
b=ma[n-1][i];
if(n-2>=0)
b^=ma[n-2][i];//n<2?
if(i-1>=0){
b^=ma[n-1][i-1];
}
if(i+1<m){
b^=ma[n-1][i+1];
}
if(a[n-1][i]=='B'){
b[m]=b[m]^1;
}
}
if(gauss(qu,m)){
bitset<N>sol;
for(int i=0;i<m;i++){
sol[i]=qu[i][m];
}
sol[m]=1;
vector<vector<char>>ans(n);
for(int i=0;i<n;i++){
ans[i]=vector<char>(m);
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
int t=(sol&ma[i][j]).count();
if(t&1){
ans[i][j]='P';
}
else {
ans[i][j]='A';
}
}
}
if(!f){
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
putchar(ans[i][j]);
putchar(' ');
}
putchar('
');
}
}
else {
for(int j=0;j<m;j++){
for(int i=0;i<n;i++){
putchar(ans[i][j]);
putchar(' ');
}
putchar('
');
}
}
}
else {
printf("IMPOSSIBLE");
}
}
Problem F:
solver Hugin
多边形面积套公式就好了
Problem G:
solver Hugin
考虑反图,不能交换的物种之间的顺序已经固定了,那么做一遍拓扑排序就好。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
const int M=220;
vector<int>pos[M];
bitset<M>E[M];
bitset<M>F[M];
vector<int>ans;vector<string>name;
int ID(string x){
return lower_bound(name.begin(),name.end(),x)-name.begin();
}
int main()
{
int m,k,n;
ios::sync_with_stdio(false);
cin.tie(0);
cin>>m>>k>>n;
name.resize(m);
for(int i=0;i<m;i++){
cin>>name[i];
}
memset(E,-1,sizeof(E));
for(int i=0;i<m;i++){
E[i][i]=0;
}
sort(name.begin(),name.end());
while(k--){
int u,v;
string a,b;
cin>>a>>b;
u=ID(a),v=ID(b);
E[u][v]=E[v][u]=0;
}
for(int i=0;i<n;i++){
string s;
cin>>s;
pos[ID(s)].push_back(i);
}
for(int i=0;i<m;i++){
pos[i].push_back(n);
reverse(pos[i].begin(),pos[i].end());
}
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
if(pos[i].back()>pos[j].back()){
F[i][j]=1;
}
}
}
while(ans.size()<n){
int id=0;
while((id<m&&pos[id].back()==n)||(E[id]&F[id]).any()){
id++;
}
ans.push_back(id);
{
pos[id].pop_back();
for(int i=0;i<m;i++){
F[i][id]=0;
F[id][i]=0;
if(pos[i].back()>pos[id].back()){
F[i][id]=1;
}
else if(pos[id].back()>pos[i].back()){
F[id][i]=1;
}
}
}
}
for(int i=0;i<n;i++){
cout<<name[ans[i]]<<' ';
}
}
Problem H
solver Hugin
找到循环节后分段打表。
Problem I
水题
Problem J:
solver Hugin
最小值一定是子树的根,相同的值可以组成二叉树。注意不直接相邻的值也可能在树上相邻。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3000000,P=1000000007;
int f[N+5],inv[N+5],inv2[N];
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1){
res=1ll*res*a%P;
}
a=1ll*a*a%P;
}
return res;
}
int C(int n,int m){
return 1ll*f[n]*inv[m]%P*inv[n-m]%P;
}
signed main()
{
f[0]=1;
for(int i=1;i<=N;i++){
f[i]=1ll*f[i-1]*i%P;
}
inv[N]=qpow(f[N],P-2);
for(int i=N-1;i>=1;i--){
inv[i]=inv[i+1]*(i+1ll)%P;
assert(1ll*f[i]*inv[i]%P==1);
}
inv2[1]=1;
for(int i=2;i<=N;i++){
inv2[i]=1ll*(P-P/i)*inv2[P%i]%P;
assert(1ll*i*inv2[i]%P==1);
}
int n;
cin>>n;
int ans=1;
inv[0]=1;
stack<int>st;
while(n--){
int x,cur;
cin>>x;
cur=x;
int c=1;
while(!st.empty()&&x<st.top()){
if(st.top()==cur)c++;
else {
ans=1ll*ans*C(2*c,c)%P*inv2[c+1]%P;
cur=st.top(),c=1;
}
st.pop();
}
st.push(x);
ans=1ll*ans*C(2*c,c)%P*inv2[c+1]%P;
}
int cur=-1,c=1;
while(!st.empty()){
if(cur==st.top())c++;
else {
ans=1ll*ans*C(2*c,c)%P*inv2[c+1]%P;
cur=st.top(),c=1;
}
st.pop();
}
ans=1ll*ans*C(2*c,c)%P*inv2[c+1]%P;
cout<<ans;
}
Problem K
solver Hugin
就是考虑和T直接相邻的点能否达到另一个。用了一些比较垃圾的做法,tarjan缩点后dp可达点的数量,其实可以一遍dfs搞定。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5;
vector<int>G[N];
vector<int>R[N];
int dfn[N],low[N],col[N];
stack<int>st;
bool instack[N];
int dfn_clock,ccnt;
void tarjan(int u){
dfn[u]=low[u]=++dfn_clock;
st.push(u),instack[u]=true;
for(int v:G[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
++ccnt;
while(instack[u]){
int v=st.top();
col[v]=ccnt;
instack[v]=false;
st.pop();
}
}
}
vector<int>G2[N];
int w[N],dp[N];
bool vis[N];
void DP(int u){
vis[u]=true;
dp[u]=w[u];
for(auto v:G2[u]){
if(!vis[v])DP(v);
dp[u]+=dp[v];
}
}
int main()
{
int n,m,T;
cin>>n>>m>>T;
vector<int>chk;
while(m--){
int u,v;
cin>>u>>v;
if(v==T)
chk.push_back(u);
else
G[u].push_back(v);
}
vector<int>ans;
for(int i=0;i<n;i++){
if(i==T)continue;
if(!dfn[i])
tarjan(i);
}
for(int i=0;i<n;i++){
if(i==T)continue;
for(int v:G[i]){
if(col[i]==col[v]){
continue;
}
G2[col[i]].push_back(col[v]);
}
}
for(auto i:chk){
w[col[i]]++;
}
for(int i=1;i<=ccnt;i++){
if(!vis[i]){
DP(i);
}
}
for(auto i:chk){
if(dp[col[i]]==1){
ans.push_back(i);
}
}
cout<<ans.size()<<endl;
for(auto i:ans){
cout<<i<<'
';
}
}
Problem L:
solver Hugin
博弈题,考虑状压做。可以发现湿地周围的格子够成的连通块大小不超过20,而且两片湿地的距离至少为3保证了游戏独立。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
typedef pair<int,int>pii;
int n;
char ma[10][10];
bool vis[10][10];
int id[10][10];
vector<pii> rev;
void get(pii cur,vector<pii>&v){
for(int i=0;i<4;i++){
pii to={cur.first+dx[i],cur.second+dy[i]};
if(to.first<0||to.first>=n||to.second<0||to.second>=n||vis[to.first][to.second]){
continue;
}
v.push_back(to);
}
}
map<int,int>sg;
int divide(pii cur){
queue<pii>q;
q.push(cur);
int ans=0;
while(!q.empty()){
vector<pii>to;
pii cur=q.front();
vis[cur.first][cur.second]=1;
ans|=1ll<<id[cur.first][cur.second];
get(cur,to);
q.pop();
for(pii v:to){
if(id[v.first][v.second]!=-1){
q.push(v);
}
}
}
return ans;
}
int SG(int st){
//cout<<st<<endl;
if(st==0)
return 0;
if(sg.count(st)){
return sg[st];
}
bool mex[64]{};
int n=__lg(st);
for(int i=0;i<=n;i++){
if(st&(1ll<<i)){
vector<pii>to;
get(rev[i],to);
int nst=st^(1ll<<i);
for(pii x:to){
int j=id[x.first][x.second];
if(j==-1)continue;
if(nst&(1ll<<j)){
nst^=1ll<<j;
}
}
mex[min(SG(nst),n)]=1;
}
}
for(int i=0;;i++){
if(!mex[i]){
return sg[st]=i;
}
}
}
int bfs(pii s){
queue<pii>q;
q.push(s);
vector<pii>c;
while(!q.empty()){
pii cur=q.front();
q.pop();
vis[cur.first][cur.second]=true;
vector<pii>to;
get(cur,to);
for(pii v:to){
char ch=ma[v.first][v.second];
if(ch=='*'){
q.push(v);
}
else if(ch=='.'){
vis[v.first][v.second]=1;
c.push_back(v);
}
}
}
memset(id,-1, sizeof(id));
rev.resize(c.size());
for(int i=0;i<c.size();i++){
pii cur=c[i];
id[cur.first][cur.second]=i;
vis[cur.first][cur.second]=0;
rev[i]=cur;
}
vector<int>v;
for(int i=0;i<c.size();i++){
pii cur=c[i];
if(!vis[cur.first][cur.second]){
v.push_back(divide(cur));
}
}
for(int i=0;i<c.size();i++){
pii cur=c[i];
vis[cur.first][cur.second]=0;
}
if(v.size()>1){
int ans=0;
sg.clear();
for(int i:v){
ans^=SG(i);
}
return ans;
}
sg.clear();
return SG((1ll<<c.size())-1);
}
signed main()
{
int ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%s",ma[i]);
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(vis[i][j])continue;
if(ma[i][j]=='*'){
int t=bfs({i,j});
ans^=t;
}
}
}
printf("%s player will win
",ans?"First":"Second");
}