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
第八次训练没什么特别大的感触,只意识到自己应该多收集些板子,免得再遇到一些模板题半天下不了手
有n个点,每个点可以放0,1,2,3四种数,现在给出m个限制[l,r,x]要求[l,r]内只有x种数,问方案数(n,m<=100)
本以为是签到题,一个状压走起,打完觉着不太对,怎么样例过不了呀……差点把自己给送了
直接状压是不行的,因为会重复考虑情况
我们设f[i][x][y][z][k]表示前i个数,0-3最后分别位于x,y,z,k位置,但显然有一个数必须是i,所以去掉一维,同时我们考虑剩下的数的位置从大到小排即(x>y>z),那么暴力枚举更新即可
#include
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a
#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);
}
}
给你0-n辆车的长度li、车头距离终点si、最大速度vi,保证si≥si+1+li+1,每辆车在遇到前面有车之前一直会已最大速度行驶,问0号车车头到达终点的时间
以后拷上一题的初始化一定要去LL和重新设置数据范围
假如只有一辆车,时间是s0/v0
假如有两辆车,有两种情况:1、自己奇慢s0/v0 2、前面的大兄弟奇慢,自己追上后还得以它的龟速走,过线后再过一个它的车长,无论自己跑多快,最后还是得以前一辆车的节奏来(s1+l1)/v1
……(你是否发现了什么?)
我们取 m a x ( ( s j + ∑ i = 0 j l j ) / v j ) max((sj+\sum_{i=0}^{j}lj)/vj) max((sj+∑i=0jlj)/vj)即可
#include
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a
#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);
}
}
给定一个有向图,问怎样删除一些边,使得最后1-n的最短路变大,且删除边的总和尽量小
构建出一个最短路的DAG,跑一遍最小割
#include
#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);
}
}
description
给定一个仅包含小写字母的字符串
从中选取出一个长度为k的子序列
输出字典序最小的子序列
不过子序列做出一定的限制:每个字母至少出现L[i]次至多出现R[i]次
solution
很显然我们可以贪心地想 每个位置选择符合条件的最小的字母
于是乎 问题的重点在于判断 这个位置填这个字母合不合适
经过了一段时间的思考(和提交的WA)可以发现有几种情况是不合适的:
emmm可能有一些条件是多余的
不过并没有进行尝试
所以还是全部列出来
code
#include
#include
#include
#include
#include
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;
}
给你一棵树的残缺的前序遍历和中序遍历(缺的数用0表示),问有多少种树的前序遍历和中序遍历满足条件
前序遍历好啊,直接告诉你根是什么,所以我们主要根据前序遍历来搜索,设前序遍历a[i],中序遍历b[i]
设状态f(l,r,x,y)表示当前是前序遍历[l,r]对应中序遍历的[x,y],我们对a[l]的情况分类讨论:
还有一个问题,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]个出来放到左子树,因此要乘方案数 C f [ l ] [ r ] [ x ] f [ l + 1 ] [ l + p − x ] [ x ] C_{f[l][r][x]}^{f[l+1][l+p-x][x]} Cf[l][r][x]f[l+1][l+p−x][x],其他同理。
由于一个区间算过一次就不用再算了,因此我们可以记忆化一下,设为g[l][r][x]。
#include
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a
#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);
}
}
description
(题目套了个AI的背景hhh,不过维数只有2)
y = s i g n ( x 1 ∗ w 1 + x 2 ∗ w 2 + b ) y=sign(x1*w1+x2*w2+b) y=sign(x1∗w1+x2∗w2+b)
s i g n ( t ) = { 1 t > 0 0 t = 0 − 1 t < 0 sign(t)=\begin{cases} 1 & t>0 \\ 0 & t=0 \\ -1 & t<0 \end{cases} sign(t)=⎩⎪⎨⎪⎧10−1t>0t=0t<0
给定 x 1 , x 2 , y x1,x2,y x1,x2,y(PPS:题目给的y只会是1或者-1)
问是否存在 w 1 , w 2 , b w1,w2,b w1,w2,b满足所有的“样本”
solution
(最开始没看到这个sign。。写了一个高斯消元着实丢人
因为题目给的y是1或者-1
我们就可以动点脑筋
假如有 y i = 1 并 且 y j = − 1 y_i=1 并且 y_j=-1 yi=1并且yj=−1我们就可以推导出
s i g n ( x 1 i ∗ w 1 + x 2 i ∗ w 2 + b ) > s i g n ( x 1 j ∗ w 1 + x 2 j ∗ w 2 + b ) sign(x1_i*w1+x2_i*w2+b)>sign(x1_j*w1+x2_j*w2+b) sign(x1i∗w1+x2i∗w2+b)>sign(x1j∗w1+x2j∗w2+b)
x 1 i ∗ w 1 + x 2 i ∗ w 2 + b > x 1 j ∗ w 1 + x 2 j ∗ w 2 + b x1_i*w1+x2_i*w2+b>x1_j*w1+x2_j*w2+b x1i∗w1+x2i∗w2+b>x1j∗w1+x2j∗w2+b
x 1 i ∗ w 1 + x 2 i ∗ w 2 > x 1 j ∗ w 1 + x 2 j ∗ w 2 x1_i*w1+x2_i*w2>x1_j*w1+x2_j*w2 x1i∗w1+x2i∗w2>x1j∗w1+x2j∗w2
x 1 i ∗ w 1 − x 1 j ∗ w 1 > x 2 j ∗ w 2 − x 2 i ∗ w 2 x1_i*w1-x1_j*w1>x2_j*w2-x2_i*w2 x1i∗w1−x1j∗w1>x2j∗w2−x2i∗w2
( x 1 i − x 1 j ) ∗ w 1 > ( x 2 j − x 2 i ) ∗ w 2 (x1_i-x1_j)*w1>(x2_j-x2_i)*w2 (x1i−x1j)∗w1>(x2j−x2i)∗w2
这个式子应该永远成立
然而两个变量不好处理于是乎根据高中数学导数题的常用套路,左右两边同时除以w1(记得特殊考虑w1=0的情况!)
然后就是解不等式组 看看有没有解喽
(有人可能会觉得只有一个方向肯定有解啊?)
(一定要小心处理乘除负数导致不等号方向改变的情况!)
code
#include
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;
}