A題順序
13:00 比賽開始
14:13 a dxw
14:32 j yl
14:47 c dxw
15:59 g yl
16:05 d zjl
17:32 h dxw
總結
第八次訓練沒什麼特別大的感觸,只意識到自己應該多收集些板子,免得再遇到一些模板題半天下不了手
A - Blank (dp)
description
有n個點,每個點可以放0,1,2,3四種數,現在給出m個限制[l,r,x]要求[l,r]內只有x種數,問方案數(n,m<=100)
solution
本以爲是簽到題,一個狀壓走起,打完覺着不太對,怎麼樣例過不了呀……差點把自己給送了
直接狀壓是不行的,因爲會重複考慮情況
我們設f[i][x][y][z][k]表示前i個數,0-3最後分別位於x,y,z,k位置,但顯然有一個數必須是i,所以去掉一維,同時我們考慮剩下的數的位置從大到小排即(x>y>z),那麼暴力枚舉更新即可
code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rp(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int maxn=1e2+5;
const ll mo=998244353;
struct code{
int l,r,x,y;
}a[maxn];
int n,m,i,t,j,k,l,x,y,z,p,T,r,mid,q,num;
ll f[2][maxn][maxn][maxn],ans;
bool cmp(code x,code y){
return x.r<y.r;
}
void make(int i,int x,int y,int z){
f[1-p][x][y][z]%=mo;
f[p][i][y][z]+=f[1-p][x][y][z];
f[p][i][x][z]+=f[1-p][x][y][z];
f[p][i][x][y]+=f[1-p][x][y][z];
f[p][x][y][z]+=f[1-p][x][y][z];
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
scanf("%d",&T);q=1;
while (T--){
scanf("%d%d",&n,&m);
fo(i,1,m)scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].x);
sort(a+1,a+m+1,cmp);
fo(j,0,3)f[0][0][0][0]=1;
p=0;num=1;a[m+1].r=n+1;ans=0;
fo(i,0,n){
p=1-p;
fo(x,0,max(i,1)-1)
fo(y,0,max(x,1)-1)
fo(z,0,max(y,1)-1){
if (!f[1-p][x][y][z]) continue;
t=0;
for (l=num;a[l].r==i && !t && l<=m;l++){
q=1;
if (x>=a[l].l) q++;
if (y>=a[l].l) q++;
if (z>=a[l].l) q++;
if (q!=a[l].x) t=1;
}
if (!t){
if (i==n)ans+=f[1-p][x][y][z]%mo;
else make(i,x,y,z);
}
f[1-p][x][y][z]=0;
}
while (a[num].r==i && num<=m) num++;
}
printf("%lld\n",ans%mo);
}
}
C - Vacation (結論題?)
description
給你0-n輛車的長度li、車頭距離終點si、最大速度vi,保證si≥si+1+li+1,每輛車在遇到前面有車之前一直會已最大速度行駛,問0號車車頭到達終點的時間
solution
以後拷上一題的初始化一定要去LL和重新設置數據範圍
假如只有一輛車,時間是s0/v0
假如有兩輛車,有兩種情況:1、自己奇慢s0/v0 2、前面的大兄弟奇慢,自己追上後還得以它的龜速走,過線後再過一個它的車長,無論自己跑多快,最後還是得以前一輛車的節奏來(s1+l1)/v1
……(你是否發現了什麼?)
我們取即可
code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rp(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int maxn=1e6+5;
const ll mo=998244353;
struct code{
int s,v,l;
}a[maxn];
int n,m,i,t,j,k,l,p,T,r,mid,q,num;
double x,y,z;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
return f*s;
}
bool cmp(code x,code y){
return x.s<y.s;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
while (scanf("%d",&n)!=EOF){
fo(i,0,n)a[i].l=read();
fo(i,0,n)a[i].s=read();
fo(i,0,n)a[i].v=read();
double t=a[0].s*1.0/a[0].v,sum=0;
fo(i,1,n){
sum+=a[i].l;
t=max(t,(sum+a[i].s)*1.0/a[i].v);
}
printf("%.10lf\n",t);
}
}
D - Path (spfa+dinic)
description
給定一個有向圖,問怎樣刪除一些邊,使得最後1-n的最短路變大,且刪除邊的總和儘量小
solution
構建出一個最短路的DAG,跑一遍最小割
code
#include<bits/stdc++.h>
#define ll long long
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(t,a) for(ll t=first[a];t;t=nex[t])
using namespace std;
const ll maxn=2e4+5;
ll first[maxn],last[maxn],nex[maxn],value[maxn];
ll v[maxn],bz[maxn],dui[maxn],d[maxn];
ll n,m,i,t,j,k,l,x,y,z,num,T,ans;
void lian(ll x,ll y,ll z){
last[++num]=y;nex[num]=first[x];first[x]=num;value[num]=z;
}
void spfa(ll s){
memset(d,127,sizeof(d));d[s]=0;
ll i=0,j=1;v[j]=s;bz[s]=1;
while(i<j){
x=v[++i];
rp(t,x){
if (d[last[t]]<=d[x]+value[t])continue;
d[last[t]]=d[x]+value[t];
if (!bz[last[t]]) bz[v[++j]=last[t]]=1;
}
bz[x]=0;
}
}
ll bfs(){
memset(d,0,sizeof(d));d[1]=1;
ll i=0,j=1;v[j]=1;
while(i<j){
x=v[++i];
rp(t,x)
if (value[t] && !d[last[t]]) d[v[++j]=last[t]]=d[x]+1;
}
return d[n];
}
ll dg(ll x,ll sum){
if (x==n) return sum;
ll p=sum,k;
rp(t,x){
if (!value[t]||d[last[t]]!=d[x]+1)continue;
k=dg(last[t],min(p,value[t]));
if (k){
value[t]-=k;
value[dui[t]]+=k;
p-=k;
if (!p) break;
}
}
if (p==sum) d[x]=-1;
return sum-p;
}
int main(){
//freopen("data.in","r",stdin);
scanf("%lld",&T);
while (T--){
scanf("%lld%lld",&n,&m);
num=0;
memset(first,0,sizeof(first));
fo(i,1,m)scanf("%lld%lld%lld",&x,&y,&z),lian(x,y,z);
spfa(1);
//make DAG
fo(i,1,n)
rp(t,i)
if (d[i]+value[t]!=d[last[t]]) value[t]=0;
else lian(last[t],i,0),dui[num]=t,dui[t]=num;
//SAP
ans=0;
while (bfs())ans+=dg(1,2e14);
printf("%lld\n",ans);
}
}
G - String(貪心)
description
給定一個僅包含小寫字母的字符串
從中選取出一個長度爲k的子序列
輸出字典序最小的子序列
不過子序列做出一定的限制:每個字母至少出現L[i]次至多出現R[i]次
solution
很顯然我們可以貪心地想 每個位置選擇符合條件的最小的字母
於是乎 問題的重點在於判斷 這個位置填這個字母合不合適
經過了一段時間的思考(和提交的WA)可以發現有幾種情況是不合適的:
- 所有字母全部填上限次也填不滿剩餘的空位
- 所有字母全部填下線次也超過剩餘的空位
- 這個字母在這個位置之後沒有了
- 這個字母已經填過R次了
- 如果這個位置填了這個字母,剩餘的其他字母就算都填下限次也會超過剩餘的空位
emmm可能有一些條件是多餘的
不過並沒有進行嘗試
所以還是全部列出來
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
typedef long long LL;
const int oo = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
using namespace std;
int read() {
int f = 1, s = 0; char c = getchar();
for (; c < '0' || c>'9'; c = getchar())if (c == '-')f = -1;
for (; c >= '0' && c <= '9'; c = getchar())s = s * 10 + c - '0';
return f * s;
}
char s[N];
int f[N][26], ans_[N];
int l[26], r[26];
queue<int>q[26];
bool check(int x, int left) {
if (q[x].empty())return false;
if (r[x] == 0)return false;
int most = 0, least = 0;
for (int i = 0; i < 26; i++)
if (f[q[x].front()][i] < l[i])return false;
else {
most += min(r[i], f[q[x].front()][i]);
least += l[i];
}
if (most < left)return false;
if (least > left)return false;
if (left - 1 < least - l[x])return false;
return true;
}
void work() {
int n = strlen(s + 1);
for (int i = 0; i < 26; i++)f[n + 1][i] = 0;
for (int i = n; i >= 1; i--) {
for (int j = 0; j < 26; j++)
f[i][j] = f[i + 1][j];
f[i][s[i] - 'a']++;
}
int k = read();
for (int i = 0; i < 26; i++) {
l[i] = read(); r[i] = read();
while (q[i].empty() == false)q[i].pop();
}
for (int i = 1; i <= n; i++)q[s[i] - 'a'].push(i);
//printf("\n");
int ans;
for (ans = 0; ans < k; ans++) {
bool flag = true;
for (int i = 0; i < 26; i++)
if (check(i, k - ans ) == true) {
ans_[ans] = q[i].front();
q[i].pop();
if (l[i])l[i]--;
if(r[i])r[i]--;
flag = false;
break;
}
// printf("%d %d\n", ans, ans_[ans]);
if (flag)break;
for (int i = 0; i < 26; i++)
while (q[i].empty() == false && q[i].front() < ans_[ans])
q[i].pop();
}
if (ans < k - 1)printf("-1");
else for (int i = 0; i < k; i++)putchar(s[ans_[i]]);
printf("\n");
}
int main() {
while (scanf("%s", s + 1) != EOF)work();
return 0;
}
H - Kingdom (記憶化搜索+?)
description
給你一棵樹的殘缺的前序遍歷和中序遍歷(缺的數用0表示),問有多少種樹的前序遍歷和中序遍歷滿足條件
solution
前序遍歷好啊,直接告訴你根是什麼,所以我們主要根據前序遍歷來搜索,設前序遍歷a[i],中序遍歷b[i]
設狀態f(l,r,x,y)表示當前是前序遍歷[l,r]對應中序遍歷的[x,y],我們對a[l]的情況分類討論:
- 假如現在a[l]存在且[x,y]中存在a[l](設在b中位置p),那我們直接就分成f(l+1,l+p-x,x,p-1)和f(l+p-x+1,r,p+1,y)
- 假如現在a[l]存在但[x,y]不存在與之對應的數,那我們就考慮在[x,y]中找一個滿足遍歷要求的0和他配對
- 假如a[l]不存在,那在[x,y]中隨便一個滿足遍歷要求且在[l,r]無配對的點都可以和他配對
還有一個問題,0和0的配對的方案數,在統計在兩個遍歷[l,r][x,y]中都沒有出現過的數的個數f[l][r][x],當按照(l+1,l+p-x,x,p-1)的方式分割時,相當於把f[l][r][x]個數選出f[l+1][l+p-x][x]個出來放到左子樹,因此要乘方案數,其他同理。
由於一個區間算過一次就不用再算了,因此我們可以記憶化一下,設爲g[l][r][x]。
code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
const ll maxn=1e2+5;
const ll mo=998244353;
ll a[maxn],b[maxn],c[maxn],d[maxn],bz[maxn],f[maxn][maxn][maxn];
ll c1[maxn][maxn],g[maxn][maxn][maxn];
ll n,m,i,t,j,k,l,p,T,r,mid,q,num;
ll dg(ll l,ll r,ll x,ll y){
if (r<=l) return 1;
if (g[l][r][x]>=0) return g[l][r][x];
ll t,k,k1,num;t=k=k1=0;
memset(bz,0,sizeof(bz));num=f[l][r][x];
if (c[a[l]]){
fo(i,x,y){
if (k1<i && k<=i-x+l && i==c[a[l]])t=c1[num][f[l+1][i-x+l][x]]*dg(l+1,i-x+l,x,i-1)%mo*dg(i-x+l+1,r,i+1,y)%mo;
k=max(k,d[b[i]]);
k1=max(k1,c[a[l+1+i-x]]);
}
}else if (a[l]){
fo(i,x,y){
if (k1<i && k<=i-x+l && !b[i])t+=c1[num][f[l+1][i-x+l][x]]*dg(l+1,i-x+l,x,i-1)%mo*dg(i-x+l+1,r,i+1,y)%mo;
k=max(k,d[b[i]]);
k1=max(k1,c[a[l+1+i-x]]);
}
}else{
fo(i,x,y){
if (k1<i && k<=i-x+l){
if (b[i]){
if (!d[b[i]])t+=c1[num][f[l+1][i-x+l][x]]*dg(l+1,i-x+l,x,i-1)%mo*dg(i-x+l+1,r,i+1,y)%mo;
}else t+=num*c1[num-1][f[l+1][i-x+l][x]]%mo*dg(l+1,i-x+l,x,i-1)%mo*dg(i-x+l+1,r,i+1,y)%mo;
}
k=max(k,d[b[i]]);
k1=max(k1,c[a[l+1+i-x]]);
}
}
return g[l][r][x]=t%mo;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
scanf("%lld",&T);
c1[0][0]=1;
fo(i,1,100){
c1[i][0]=1;
fo(j,1,i) c1[i][j]=(c1[i-1][j-1]+c1[i-1][j])%mo;
}
while (T--){
scanf("%lld",&n);
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
fo(i,1,n)scanf("%lld",&a[i]),d[a[i]]=i;
fo(i,1,n)scanf("%lld",&b[i]),c[b[i]]=i;
fo(i,1,n)
fo(j,1,n){
memset(bz,0,sizeof(bz));num=k=0;
while (i+k<=n && j+k<=n){
if (!bz[a[i+k]] && a[i+k])bz[a[i+k]]=1,num++;
if (!bz[b[j+k]] && b[j+k])bz[b[j+k]]=1,num++;
f[i][i+k][j]=k+1-num;
g[i][i+k][j]=-1;
k++;
}
}
c[0]=d[0]=0;
ll ans;
ans=dg(1,n,1,n);
printf("%lld\n",ans);
}
}
HDU - 6590 Code (模擬?
description
(題目套了個AI的背景hhh,不過維數只有2)
給定(PPS:題目給的y只會是1或者-1)
問是否存在滿足所有的“樣本”
solution
(最開始沒看到這個sign。。寫了一個高斯消元着實丟人
因爲題目給的y是1或者-1
我們就可以動點腦筋
假如有我們就可以推導出
這個式子應該永遠成立
然而兩個變量不好處理於是乎根據高中數學導數題的常用套路,左右兩邊同時除以w1(記得特殊考慮w1=0的情況!)
然後就是解不等式組 看看有沒有解嘍
(有人可能會覺得只有一個方向肯定有解啊?)
(一定要小心處理乘除負數導致不等號方向改變的情況!)
code
#include<bits/stdc++.h>
typedef long long LL;
const int oo=0x3f3f3f3f;
const int N=110;
const int MOD=1e9+7;
using namespace std;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
return f*s;
}
int x1[N],x2[N],y[N];
bool work(){
int n=read();
for(int i=0;i<n;i++){
x1[i]=read();
x2[i]=read();
y[i]=read();
}
//w1>0
double L=-1LL<<31,R=1LL<<31;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(i==j)continue;
if(y[i]==1&&y[j]==-1){
int fenmu=x2[j]-x2[i];
int fenzi=x1[i]-x1[j];
if(fenmu==0)continue;
if(fenmu<0)L=max(L,1.0*fenzi/fenmu);
else R=min(R,1.0*fenzi/fenmu);
}
}
// printf("%.3lf %.3lf\n",L,R);
if(L<=R)return true;
//w1==0
L=-1LL<<31,R=1LL<<31;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(i==j)continue;
if(y[i]==1&&y[j]==-1){
int fenmu=x2[j]-x2[i];
int fenzi=0;
if(fenmu==0)continue;
if(fenmu>0)L=max(L,1.0);
else R=min(R,-1.0);
}
}
//printf("%.3lf %.3lf\n",L,R);
if(L<=R)return true;
//w1<0
L=-1LL<<31,R=1LL<<31;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(i==j)continue;
if(y[i]==1&&y[j]==-1){
int fenmu=x2[j]-x2[i];
int fenzi=x1[i]-x1[j];
if(fenmu==0)continue;
if(fenmu>0)L=max(L,1.0*fenzi/fenmu);
else R=min(R,1.0*fenzi/fenmu);
}
}
// printf("%.3lf %.3lf\n",L,R);
if(L<=R)return true;
return false;
}
int main(){
int T=read();
for(int i=1;i<=T;i++){
if(work()==true)printf("Successful!\n");
else printf("Infinite loop!\n");
}
return 0;
}