10.25 解题报告

T1

用时:1.5h

赛时 \(30\) min切了,对着错大样例调了 \(1\) h。

#include<bits/stdc++.h>
#define ll long long
#define int long long
//#define ull unsigned long long
#define lc(k) k<<1
#define rc(k) k<<1|1
#define lb lower_bound
#define orz cout<<"gzn ak ioi\n"
const int MAX=1e6+10;
const int MOD=1e9+7;
using namespace std;
inline char readchar() {
	static char buf[100000], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read() {
#define readchar getchar
	int res = 0, f = 0;
	char ch = readchar();
	for(; !isdigit(ch); ch = readchar()) if(ch == '-') f = 1;
	for(; isdigit(ch);ch = readchar()) res = (res << 1) + (res << 3) + (ch ^ '0');
	return f ? -res : res;
}
inline void write(int x) {
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
/*
一圈一圈的增长
紧挨着增长
第一圈:1
2      6
3      12
4      18
5      24
第n圈有(n-1)*6个方块 n>1
第一圈有 1 个

假设当前数目恰好可以铺满n圈 

1+\sum_{i=2}^n 6(n-1)=1+6(1+n-1)(n-1)/2=1+3n(n-1)

考虑第n+1圈剩了k个
每完全空出一条边答案减少1,第一条边需要少n+1个,2-5是n个
6是n-1个

做完了 
*/
signed main(){
	freopen("square.in","r",stdin);
	freopen("square.out","w",stdout);
	int x=read(),ans=0;
	int n=(3.0+sqrt(9.0+12.0*x-12.0))/6.0;
	int sum=3*n*(n-1)+1;//整圈的个数 
	ans=6*(n+1); 
	int k=x-sum,t=6*n;
	if(t-k>=n+1){//6
		ans--,t-=(n+1);
		if(t-k>=n){//5 
			ans--,t-=n;
			if(t-k>=n){//4
				ans--,t-=n;
				if(t-k>=n){//3
					ans--,t-=n;
					if(t-k>=n){//2
						ans--,t-=n;
						if(t-k>=n-1){
							ans--;t-=(n-1);
						}
					}
				}
			}
		}
	}
	cout<<ans;
	return 0; 
}

T2

用时: \(30\) min
期望得分:\(0\)
实际得分:\(10\)

得了十分是因为对于 \(f_i=i\) 的点,我的贪心恰好是对的。

考场弃掉是因为觉得自己读不懂题目,实际上确实是没读对题,题目中 \(i\) 号位置为空是指的真的被取空,而不是按下了 \(a_i\) 次按钮。

考虑对于每一个点 \(i\),我们由它的 \(f_i\) 向他连一条边,表示 \(f_i\) 可由 \(i\) 买到,但实际上对于 \(f_i\),一定是只用花费最小的点来买它,所以只记录一下它的后继里面 \(c\) 值最小的即可。

不难发现,由于每个点的出度最多是 \(1\)(最多只用一个点来买它),入度也最多是 \(1\)(最多只能买一个点),所以这个图是由若干条链和若干个简单环组成的。

环链之间相互独立,于是对每一条链和每一个环可以单独考虑。

对于链,叶子结点没有出度,贡献为 \(0\),其他的贡献都是后继的 \(c\) 减去这个节点的 \(d\) 再乘上再乘上 \(a\)

对于环,一定有一个点贡献为 \(0\),考虑这个点应该是哪个。

现在先假设 \(i\) 号点的贡献为 \(0\),假设它的后继是 \(v\),那么它对于没断开之前的贡献就减少了 \(c_v-d_i\),于是要找到 \(c_v-d_i\) 最小的点,将其贡献减去。

但这是不对的,因为一个点虽然只记录了一个后继,但是他可能还有收益次大的后继,这个后继的贡献也是应该算上的,假设这个节点是 \(v'\),那么删掉一个点的 \(\Delta=-(c_v-d_i)+c_{v'}-d_i=c_{v'}-c_v\),所以应当找到 \(c_{v'}-c_v\) 最小的点(前提是存在 \(v'\))。

#include<bits/stdc++.h>
#define ll long long
#define int long long
//#define ull unsigned long long
#define lc(k) k<<1
#define rc(k) k<<1|1
#define lb lower_bound
#define orz cout<<"gzn ak ioi\n"
const int MAX=1e6+10;
const int MOD=1e9+7;
using namespace std;
inline char readchar() {
	static char buf[100000], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read() {
#define readchar getchar
	int res = 0, f = 0;
	char ch = readchar();
	for(; !isdigit(ch); ch = readchar()) if(ch == '-') f = 1;
	for(; isdigit(ch);ch = readchar()) res = (res << 1) + (res << 3) + (ch ^ '0');
	return f ? -res : res;
}
inline void write(int x) {
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int n,idx,minn,f[N],c[N],d[N],w[N],a[N];
int mx[N],secmx[N],dfn[N],ans;
void dfs(int x){
	if(dfn[x]==idx){
		ans-=minn;
		return;
	}
	if(dfn[x])return;
	dfn[x]=idx;
	if(mx[x]){
		ans+=w[mx[x]]*a[x];
		minn=min(minn,w[mx[x]]-w[secmx[x]]);
		if(mx[x]^x) dfs(mx[x]);
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;++i)
	  f[i]=read(),c[i]=read(),d[i]=read(),a[i]=read();
	for(int i=1;i<=n;++i){
		w[i]=d[f[i]]-c[i];
		if(w[i]<0)continue;
		if(w[i]>w[mx[f[i]]])
		  secmx[f[i]]=mx[f[i]],mx[f[i]]=i;
		else if(w[i]>w[secmx[f[i]]])
		  secmx[f[i]]=i;
	}
	for(int i=1;i<=n;++i)
	  if(!dfn[i]) 
	    minn=INF,idx++,dfs(i);
	cout<<ans<<endl;
	return 0;
}

T3

T4

用时:\(20\) min
前面用的时间太长了,导致这题没时间细想,只拿了 \(60\) 的暴力分,其实正解也非常简单,只需要按 \(\max(dis_u,dis_v)\) 排序即可。

#include<bits/stdc++.h>
#define ll long long
#define int long long
//#define ull unsigned long long
#define lc(k) k<<1
#define rc(k) k<<1|1
#define lb lower_bound
#define orz cout<<"gzn ak ioi\n"
const int MAX=1e6+10;
const int MOD=1e9+7;
using namespace std;
inline char readchar() {
	static char buf[100000], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read() {
#define readchar getchar
	int res = 0, f = 0;
	char ch = readchar();
	for(; !isdigit(ch); ch = readchar()) if(ch == '-') f = 1;
	for(; isdigit(ch);ch = readchar()) res = (res << 1) + (res << 3) + (ch ^ '0');
	return f ? -res : res;
}
inline void write(int x) {
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int n,m,c,head[MAX],cnt;
struct node{int u,v,w,net;}e[MAX<<1],e2[MAX];
void add(int u,int v,int w){
	e[++cnt]=(node){u,v,w,head[u]};
	head[u]=cnt;
	return ;
}
priority_queue<pair<int,int> > q;
int dis[MAX],vis[MAX];
void dij(){
	for(int i=1;i<=n;i++) dis[i]=1e12;
	dis[1]=0;
	q.push(make_pair(0,1));
	while(!q.empty()){
		pair<int,int> f=q.top();q.pop();
		int u=f.second;
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=e[i].net){
			int v=e[i].v,w=e[i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
	return ;
}
signed main(){
//	freopen("subway.in","r",stdin);
//	freopen("subway.out","w",stdout);
	n=read(),m=read(),c=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		add(u,v,w);add(v,u,w);
	}
	int mx=0;
	dij();
	for(int i=1;i<=n;i++)
		mx=max(mx,dis[i]);
	int zong=0; 
	for(int i=2;i<=cnt;i+=2) e2[i/2]=e[i],zong+=e[i].w;
	sort(e2+1,e2+1+m,[](node x,node y){
		return max(dis[x.u],dis[x.v])<max(dis[y.u],dis[y.v]);
	});
	int calc=e2[1].w,ans=1e18;
	for(int x=0,j=1;x<=mx;x++){
		int sum=c*x;
		while(max(dis[e2[j].u],dis[e2[j].v])<=x&&j<m) ++j,calc+=e2[j].w;
		if(max(dis[e2[j].u],dis[e2[j].v])>x) calc-=e2[j].w,j--; 
		ans=min(ans,sum+zong-calc);
	}
	cout<<ans;
	return 0;
}

posted @ 2022-10-26 11:23  wapmhac  阅读(18)  评论(0编辑  收藏  举报