2019杭电多校第一场

1001 Blank

d p [ i ] [ j ] [ k ] [ t ] dp[i][j][k][t] dp[i][j][k][t]表示 0 , 1 , 2 , 3 0,1,2,3 0,1,2,3出现的位置排序后为 i , j , k , t i,j,k,t i,j,k,t的方案数
枚举第 t + 1 t+1 t+1位的情况进行转移
对于限制情况,固定右端点,暴力枚举所有状态,把所有非法状态清零

#include<iostream>
#include<vector>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define mp make_pair
using namespace std;
int n,m,res,i,l,r,x,t,tt,j,k,p;
int f[105][105][105][2];
vector<pair<int,int>> d[105];
int main()
{
    int mod = 998244353;
    int T;
    scanf("%d",&T);
    while (T--)
    {
        fo(i,1,n) d[i].clear();
        scanf("%d%d",&n,&m);
        fo(i,1,m)
        {
            scanf("%d%d%d",&l,&r,&x);
            d[r].push_back(mp(l,x));
        }
        memset(f,0,sizeof(f));
        f[0][0][0][0] = 1;
        fo(t,1,n)
        {
            tt = t & 1;
            fo(i,0,t) fo(j,i,t) fo(k,j,t) f[i][j][k][tt] = 0;
            fo(i,0,t) fo(j,i,t) fo(k,j,t)
            {
                f[j][k][t-1][tt] = (f[j][k][t-1][tt] + f[i][j][k][tt^1]) % mod;
                f[i][k][t-1][tt] = (f[i][k][t-1][tt] + f[i][j][k][tt^1]) % mod;
                f[i][j][t-1][tt] = (f[i][j][t-1][tt] + f[i][j][k][tt^1]) % mod;
                f[i][j][k][tt] = (f[i][j][k][tt] + f[i][j][k][tt^1]) % mod;
            }
            fo(i,0,t) fo(j,i,t) fo(k,j,t)
            for (p = 0;p < d[t].size(); p++)
            {
                l = d[t][p].first; x = d[t][p].second;
                if ((i>=l)+(j>=l)+(k>=l)+(t>=l) != x) f[i][j][k][tt] = 0;
            }
        }
        res = 0;
        fo(i,0,n) fo(j,i,n) fo(k,j,n)
            res = (res + f[i][j][k][n&1]) % mod;
        cout<<res<<endl;
    }
    return 0;
}

1004 Vacation

从近到远不断求出每辆车的速度函数
显然每辆车的速度是一个阶梯形(每次撞上前一辆车速度都会变小)
如何根据第 i i i辆车的图像得到 i + 1 i+1 i+1辆车的图像?
先画出一条水平线表示初始速度,然后求什么时候两条曲线之间的面积等于两车初始距离(即什么时候追上),之后的速度图像一致
当然这个面积也可以是负数(表示前车开始过快,距离拉大)
时间复杂度为 O ( n ) O(n) O(n)
记录图像的“阶梯拐点”(速度变化点)即可

#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define N 5000000
using namespace std;
int n,i,base,head,flag;
int l[N],s[N],v[N],p[N];
double t[N];
double d,dd;
int main()
{
    while (~scanf("%d",&n))
    {
        n++;
        fd(i,n,1) scanf("%d",&l[i]);
        fd(i,n,1) scanf("%d",&s[i]);
        fd(i,n,1) scanf("%d",&v[i]);
        
        base = 100005;
        t[base] = 0; p[base] = v[1];
        t[base+1] = 1e14; p[base+1] = 0;
        head = base;
        fo(i,2,n)
        {
            d = s[i] - (s[i-1] + l[i-1]);
            flag = 0;
            while (d >= (t[head+1]-t[head]) * (v[i] - p[head]))//注意这里的等号
            {
                if (head == base) {flag = 1; break;}
                d -= (t[head+1] - t[head]) * (v[i] - p[head]);
                head++;
            }
            if (flag == 1)
            {
                t[base] = 0; p[base] = v[i];
                continue;
            }
            dd = d / (v[i] - p[head]);
            t[head] += dd;
            head--;
            t[head] = 0; p[head] = v[i];
        }
        d = s[n];
        while (d > (t[head+1]-t[head]) * p[head])
        {
            d -= (t[head+1] - t[head]) * p[head];
            head++;
        }
        dd = d / p[head];
        t[head] += dd;
        printf("%.10lf\n",t[head]);
    }
    return 0;
}

1005 Path

先保留所有最短路的边,即满足 d u − d v = w d_u-d_v=w dudv=w的边,然后跑最小割

1009 String

暴力枚举每一位的字母,合法的依据是后缀能否满足条件

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define N 500000
using namespace std;
int n,i,j,T,head,nextpos,flag,suffix;
char ch[N];
queue<int> pos[30];
int a[N],l[30],r[30],cnt[N][30];
queue<int> res;
int resflag;
int main()
{
    while (~scanf("%s",ch+1))
    {
    
    n = strlen(ch+1);
    fo(i,1,n) a[i] = ch[i] - 'a' + 1;
    scanf("%d",&T);
    fo(i,1,26) scanf("%d%d",&l[i],&r[i]);
    fo(i,1,n) fo(j,1,26) cnt[i][j] = cnt[i-1][j] + (a[i] == j);
    fo(i,1,26) while (!pos[i].empty()) pos[i].pop();
    while (!res.empty()) res.pop();
    fo(i,1,n) pos[a[i]].push(i);
    head = 0;
    while (T--)
    {
        resflag = 0;
        fo(i,1,26)
        {
            if (r[i] == 0) continue;
            while (!pos[i].empty() && pos[i].front() <= head) pos[i].pop();
            if (pos[i].empty()) continue;
            
            nextpos = pos[i].front();
            flag = 0; suffix = 0;
            fo(j,1,26)
            {
                suffix += l[j];
                if (l[j] && j == i) suffix--;
                if (cnt[n][j] - cnt[nextpos-1][j] < l[j]) {flag = 1; break;}
                if (T < suffix) {flag = 1; break;}
            }
            if (flag) continue;
            if (l[i]) l[i]--; r[i]--;
            res.push(i);
            head = nextpos;
            resflag = 1;
            break;
        }
        if (resflag == 0) {cout<<-1; break;}
    }
    if (resflag == 1)
    while (!res.empty()) {cout<<(char)('a'+res.front()-1); res.pop();}
    cout<<endl;
    }
    return 0;
}

1013 Code

问题转化为能否用一条直线分开两类点
这个问题等价于两个凸包是否相交
两两比较两个凸包上的边是否相交即可
注意先判断所有点的凸包,特判内含的情况

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
const double eps=1e-10;
const int maxn=300;
struct point {
    double x,y;
    int id;
}pnt[maxn],pnt1[maxn],pnt2[maxn],res1[maxn],res2[maxn];
bool mult(point sp,point ep,point op) {
    return (sp.x-op.x)*(ep.y-op.y)>=(ep.x-op.x)*(sp.y-op.y);
}
bool operator <(const point &l,const point &r) {
    return l.y<r.y||(l.y==r.y&&l.x<r.x);
}
int graham(point pnt[],int n,point res[]) {
    int len,k=0,top=1;
    memset(res,0,sizeof res);
    sort(pnt,pnt+n);
    if(n==0) return 0;res[0]=pnt[0];
    if(n==1) return 1;res[1]=pnt[1];
    if(n==2) return 2;res[2]=pnt[2];
    for(int i=2; i<n; i++) {
        while(top&&mult(pnt[i],res[top],res[top-1])) top--;
        res[++top]=pnt[i];
    }
    len=top;res[++top]=pnt[n-2];
    for(int i=n-3; i>=0; i--) {
        while(top!=len&&mult(pnt[i],res[top],res[top-1])) top--;
        res[++top]=pnt[i];
    }
    return top;
}
bool inter(point a,point b,point c,point d)
{
    if(min(a.x,b.x)>max(c.x,d.x)||
       min(a.y,b.y)>max(c.y,d.y)||
       min(c.x,d.x)>max(a.x,b.x)||
       min(c.y,d.y)>max(a.y,b.y)) return 0;
    double h,i,j,k;
    h=(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
    i=(b.x-a.x)*(d.y-a.y)-(b.y-a.y)*(d.x-a.x);
    j=(d.x-c.x)*(a.y-c.y)-(d.y-c.y)*(a.x-c.x);
    k=(d.x-c.x)*(b.y-c.y)-(d.y-c.y)*(b.x-c.x);
    return h*i<=eps&&j*k<=eps;
}
int main() {
    int t,n,id;
    int x,y;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        memset(pnt,0,sizeof pnt);
        int n1=0,n2=0;
        for(int i=0;i<n;i++){
            scanf("%d%d%d",&x,&y,&id);
            if (id == -1) id = 0;
            pnt[i].x=x;pnt[i].y=y;pnt[i].id=id;
            if(id==0){
                pnt1[n1].x=x;pnt1[n1].y=y;pnt1[n1++].id=id;
            }
            else{
                pnt2[n2].x=x;pnt2[n2].y=y;pnt2[n2++].id=id;
            }
        }
        if(n==1||n==2){
            cout<<"Successful!"<<endl;continue;
        }
        if(n1==n||n2==n){cout<<"Successful!"<<endl;continue;}
        int m=graham(pnt,n,res1);
        int cnt=0;
        for(int i=0;i<m;i++) if(res1[i].id==1) cnt++;
        if(cnt==0||cnt==m){cout<<"Infinite loop!"<<endl;continue;}
        int m1=graham(pnt1,n1,res1);
        int m2=graham(pnt2,n2,res2);
        int flag=0;
        for(int i=0;i<=m1-1;i++){
            point a=res1[i],b;
            if(i==m1-1) b=res1[0];
            else b=res1[i+1];
            for(int j=0;j<=m2-1;j++){
                point c=res2[j],d;
                if(j==m2-1) d=res2[0];
                else d=res2[j+1];
                if(inter(a,b,c,d)){flag=1;break;}
            }
            if(flag) break;
        }
        if(flag) cout<<"Infinite loop!"<<endl;
        else cout<<"Successful!"<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值