爆零了...爽到
T1 题意简述:jzoj3470
Description
给定一个n个点m条边的有向图,有k个标记点,要求从规定的起点按任意顺序经过所有标记点到达规定的终点,问最短的距离是多少。
Input
第一行5个整数n、m、k、s、t,表示点个数、边条数、标记点个数、起点编号、终点编号。
接下来m行每行3个整数x、y、z,表示有一条从x到y的长为z的有向边。
接下来k行每行一个整数表示标记点编号。
接下来m行每行3个整数x、y、z,表示有一条从x到y的长为z的有向边。
接下来k行每行一个整数表示标记点编号。
Output
输出一个整数,表示最短距离,若没有方案可行输出-1。
Data Constraint
20%的数据n<=10。
50%的数据n<=1000。
另有20%的数据k=0。
100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。
50%的数据n<=1000。
另有20%的数据k=0。
100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。
解题思路:k遍dijkstra或SPFA+爆搜即可。由于k只有10,因此比状压dp还要快些。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f3f3f3f3f #define ll long long using namespace std; ll n,m,k,s,t,cnt,ans=INF,head[50001]; ll mp[50001],pnt[11],len[20][20]; ll dis[50001],vis[50001]; struct uio{ ll nxt,to,val; }edge[100001]; void add(ll x,ll y,ll z) { edge[++cnt].nxt=head[x]; edge[cnt].to=y; edge[cnt].val=z; head[x]=cnt; } void spfa(ll x) { memset(dis,0x3f,sizeof(dis)); queue<ll> que; while(!que.empty()) que.pop(); dis[x]=0; vis[x]=1; que.push(x); while(!que.empty()) { ll now=que.front(); que.pop(); vis[now]=0; for(ll i=head[now];i;i=edge[i].nxt) { ll y=edge[i].to; if(dis[y]>dis[now]+edge[i].val) { dis[y]=dis[now]+edge[i].val; if(!vis[y]) vis[y]=1,que.push(y); } } } } void dfs(ll x,ll now,ll l) { if(l>ans) return; if(x==k) { ans=min(ans,l+len[now][k+1]); return; } for(ll i=1;i<=k;i++) if(!vis[i]&&i!=now&&len[now][i]!=INF) vis[i]=1,dfs(x+1,i,l+len[now][i]),vis[i]=0; } int main() { scanf("%lld%lld%lld%lld%lld",&n,&m,&k,&s,&t); for(ll i=1;i<=m;i++) { ll u,v,w; scanf("%lld%lld%lld",&u,&v,&w); add(u,v,w); } for(ll i=1;i<=k;i++) { scanf("%lld",&pnt[i]); mp[pnt[i]]=++mp[0]; } spfa(s); for(ll i=1;i<=k;i++) len[0][mp[pnt[i]]]=dis[pnt[i]]; if(!k) { if(dis[t]==INF) printf("-1\n"); else printf("%lld\n",dis[t]); return 0; } for(ll i=1;i<=k;i++) { spfa(pnt[i]); len[mp[pnt[i]]][k+1]=dis[t]; for(ll j=1;j<=k;j++) len[mp[pnt[i]]][mp[pnt[j]]]=dis[pnt[j]]; } dfs(0,0,0); if(ans==INF) printf("-1\n"); else printf("%lld\n",ans); return 0; }
T2 题意简述:jzoj3487
Description
万老师听说某大国很流行穿越,于是他就想写一个关于穿越的剧本。
闲话休提。话说老师穿越到了某一个剑与魔法的大陆。因为如此这般,所以老师从维娜艾那里得到了预言。老师一共被告知了若干件按顺序结算的事件。这些事件分为两类:战役事件(CASE)、穿越回去事件(END)。战役事件可以选择是否参加,参加了之后会获得一定的金钱。每个END事件发生需要至少参加一定数量的战役事件。特别的是,END事件如果满足要求就会强制发生。老师希望在大陆玩个够,所以他要求只有最后一个END事件会发生。老师希望获得最多的金钱,所以求助于你。
闲话休提。话说老师穿越到了某一个剑与魔法的大陆。因为如此这般,所以老师从维娜艾那里得到了预言。老师一共被告知了若干件按顺序结算的事件。这些事件分为两类:战役事件(CASE)、穿越回去事件(END)。战役事件可以选择是否参加,参加了之后会获得一定的金钱。每个END事件发生需要至少参加一定数量的战役事件。特别的是,END事件如果满足要求就会强制发生。老师希望在大陆玩个够,所以他要求只有最后一个END事件会发生。老师希望获得最多的金钱,所以求助于你。
Input
第一行一个数N,表示输入文件有多少行。
接下来每一行用空格隔开一个字符和一个整数。字符为“c”表示战役事件,接下来的整数表示这次涨RP顺带有多少钱;字符为“e”表示穿越回去事件,接下来的整数代表至少要涨多少RP。最后一个事件保证是END事件。
接下来每一行用空格隔开一个字符和一个整数。字符为“c”表示战役事件,接下来的整数表示这次涨RP顺带有多少钱;字符为“e”表示穿越回去事件,接下来的整数代表至少要涨多少RP。最后一个事件保证是END事件。
Output
第一行一个整数,最多金钱数目。
若不可能则输出-1。
若不可能则输出-1。
Data Constraint
30%的数据满足 N<=20
60%的数据满足 N<=1,000
100%的数据满足 N<=200,000
每次涨RP事件赏金不超过10,000
穿越事件的要求不超过200,000
60%的数据满足 N<=1,000
100%的数据满足 N<=200,000
每次涨RP事件赏金不超过10,000
穿越事件的要求不超过200,000
解题思路:小根堆维护所有事件即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #include<vector> #define INF 0x3f3f3f3f3f3f3f3f #define ll long long using namespace std; int n,ans; priority_queue<int,vector<int>,greater<int> > que; int main() { scanf("%d\n",&n); for(int i=1;i<n;i++) { char c;int u; scanf("%c %d\n",&c,&u); if(c=='c') que.push(u); else while(que.size()>=u) que.pop(); } char c;int u; scanf("%c %d",&c,&u); if(que.size()<u) {printf("-1\n");return 0;} while(!que.empty()) ans+=que.top(),que.pop(); printf("%d\n",ans); return 0; }
T3 题意简述:jzoj3493
Description
平面上有n个点,求出用这些点可以构成的三角形数。
Input
第一行一个整数n。
接下来n行,每行两个整数,表示点的坐标。
接下来n行,每行两个整数,表示点的坐标。
Output
输出仅一个整数,表示所求答案。
Data Constraint
对于50%的数据,n<=300。
对于100%的数据,n<=3000,坐标的绝对值不超过10^4,保证没有重合的点。
对于100%的数据,n<=3000,坐标的绝对值不超过10^4,保证没有重合的点。
解题思路:枚举每个点,把其它点与这个点的斜率计算出来,将斜率相同的合并,计算组合数,用总方
案数减去计算出的组合数即可。
如何去重?只需在第二重循环时只枚举i+1~n的点且只把包含i的不合法情况去除即可。
考虑以下例子:编号为1 2 3 4的点排成一条直线。
枚举到1时,只把包含1的不合法情况去除。在本例子中,即去除(1,2,3)(1,2,4)(1,3,4)。
枚举到2时,只把包含2的不合法情况去除。在本例子中,即去除(2,3,4)。
以此类推,只需计算C(n,3)-∑(i=1~n)∑(j=i+1~n)C(k,2) 其中k表示相同斜率的数量。
感谢ErkkiErkko大佬和Menteur_Hxy大佬!!!
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define INF 0x3f3f3f3f3f3f3f3f #define ll long long using namespace std; ll n,cnt,ans; double k[10001],a[10001],b[10001]; int main() { freopen("triangle.in","r",stdin); freopen("triangle.out","w",stdout); scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%lf%lf",&a[i],&b[i]); for(ll i=1;i<=n;i++) { cnt=0,memset(k,0xc0,sizeof(k)); for(ll j=i+1;j<=n;j++) if(a[i]==a[j]) k[++cnt]=INF; else k[++cnt]=(b[j]-b[i])/(a[j]-a[i]); sort(k+1,k+1+cnt); ll tmp=1; for(ll j=2;j<=cnt+1;j++) if(k[j]==k[j-1]) tmp++; else ans+=tmp*(tmp-1)/2,tmp=1; } printf("%lld",n*(n-1)*(n-2)/6-ans); return 0; }