ICPC Mid-Central USA Region 2019 题解

队友牛逼!带我超神!蒟蒻的我还是一点一点的整理题吧...

Dragon Ball I

这个题算是比较裸的题目吧....学过图论的大概都知道应该怎么做。题目要求找到七个龙珠的最小距离。很明显就是7个龙珠先后去的排列,然后用dijkstra预处理出来每个龙珠到所有其他的点的最短距离啊。最后dfs暴力枚举排列统计答案就行。这里有一些小的细节问题题目中最大的距离为2e9,所以0x3f可能有点不够,还真是细节决定成败啊,不亏我Wrong了一发!

//不等,不问,不犹豫,不回头.
#include<bits/stdc++.h>
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=200010;
int n,m,link[N],id[10],tot,vis[N],d[10][N];
struct bian{ll y,v,next;}a[N<<1]; 
ll ans=1e18;
priority_queue<pair<ll,int> >q;

inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}

inline void add(int x,int y,int v)
{
	a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot; 
}

inline void dijkstra(int s)
{
	rep(i,1,n) d[s][i]=2e9;
	memset(vis,0,sizeof(vis));
	d[s][id[s]]=0;
	q.push({0,id[s]});
	while(q.size())
	{
		int x=q.top().second;q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=link[x];i;i=a[i].next)
		{
			int y=a[i].y;
			if(d[s][y]>d[s][x]+a[i].v)
			{
				d[s][y]=d[s][x]+a[i].v;
				q.push({-d[s][y],y});
			}
		}
	}
}

inline void dfs(int now,int cnt,ll s)
{
	if(cnt==7) 
	{
		ans=min(ans,s);
		return;
	}
	rep(i,1,7) 
	{
		if(!vis[i])
		{
			vis[i]=1;
			dfs(i,cnt+1,s+d[now][id[i]]);
			vis[i]=0;
		}
	}
}

int main()
{
    //freopen("1.in","r",stdin);
	get(n);get(m);
	rep(i,1,m)
	{
		int get(x),get(y),get(v);
		add(x,y,v);add(y,x,v);
	}
	id[0]=1;
	rep(i,1,7) get(id[i]);
	rep(i,0,7) dijkstra(i);
	rep(i,1,7)
	{
		if(d[0][id[i]]==2e9) 
		{
			puts("-1");
			return 0;
		}
	}
	memset(vis,0,sizeof(vis));
	dfs(0,0,0);
	putl(ans);
    return (0^_^0);
}
//以吾之血,铸吾最后的亡魂.

Farming Mars

这个算是比较裸的数据结构的题了...简化下题意就是:给你一个序列,有询问,问一个区间中有没有一个数的个数大于等于区间的一半(向上取整).先不想那么多,看到10000的范围,我这暴脾气,先打个暴力试试,\(O(n^2)\)闯天下啊,真过了,赛后就不能这样敷衍了事了。仔细想想不就是权值计数吗?区间查询最大值,写个主席树不就行了?...太水了题...不对不对,这不太行啊,主席树没法维护区间最大值啊...假了假了假了...原来小丑竟是我自己...等等等..等等,这个题问的不是区间众数的次数吗?分块啊!还是分块大法好!莫队!所有\(O(n\sqrt n)\)的数据结构都行。
先放个暴力的代码:

//不等,不问,不犹豫,不回头.
#include<bits/stdc++.h>
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=10010;
int n,m,a[N],ct[N],num;
db b[N],t[N];
 
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
 
inline int find(db s) {return lower_bound(b+1,b+num+1,s)-b;}
 
inline void init()
{
    get(n);get(m);
    rep(i,1,n) cin>>t[i],b[i]=t[i];
    sort(b+1,b+n+1);
    num=unique(b+1,b+n+1)-b-1;
    rep(i,1,n) a[i]=find(t[i]);
}
 
int main()
{
    //freopen("1.in","r",stdin);
    init();
    rep(i,1,m)
    {
        int get(l),get(r);
        rep(j,l,r) ct[a[j]]++;
        bool flag=false;
        rep(j,l,r) if(ct[a[j]]>=((r-l+1)/2)+1)
        {
            flag=true;
            break;
        }
        if(flag) puts("usable");
        else puts("unusable");
        rep(j,l,r) ct[a[j]]=0;
    }
    return (0^_^0);
}
//以吾之血,铸吾最后的亡魂.

回滚莫队的代码,分块由于前不久练过,就不再写一遍了,真的很费脑细胞...

//不等,不问,不犹豫,不回头.
#include<bits/stdc++.h>
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=10010;
int a[N],num,n,m,ans[N];
int belong[N],bl[N],br[N],block,bnum,cnt1[N],cnt2[N];
db t[N],b[N]; 
struct wy{int l,r,id;}q[N];

inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}

inline int find(db s) {return lower_bound(b+1,b+num+1,s)-b;}

inline void build()
{
	block=sqrt(n);bnum=n/block;
	if(n%block) bnum++;
	rep(i,1,n) belong[i]=(i-1)/block+1;
	rep(i,1,bnum) bl[i]=(i-1)*block+1,br[i]=i*block;
	br[bnum]=n;
}

inline bool cmp(wy a,wy b)
{
	return (belong[a.l]!=belong[b.l]?belong[a.l]<belong[b.l]:a.r<b.r);
}

inline void init()
{
    get(n);get(m);
    rep(i,1,n) cin>>t[i],b[i]=t[i];
    sort(b+1,b+n+1);
    num=unique(b+1,b+n+1)-b-1;
    rep(i,1,n) a[i]=find(t[i]);
    rep(i,1,m) get(q[i].l),get(q[i].r),q[i].id=i;
    build();
    sort(q+1,q+m+1,cmp);
}

inline void solve()
{
	int i=1;
	rep(j,1,bnum)
	{
		int now=0;
		int l=br[j]+1,r=br[j];
		memset(cnt1,0,sizeof(cnt1));
		for(;i<=m&&belong[q[i].l]==j;++i)
		{
			int temp;
			int ql=q[i].l,qr=q[i].r;
			if(belong[ql]==belong[qr])
			{
				temp=0;
				rep(k,ql,qr) cnt2[a[k]]=0;
				rep(k,ql,qr) cnt2[a[k]]++,temp=max(temp,cnt2[a[k]]);
				ans[q[i].id]=(temp>=(qr-ql+1)/2+1?1:0);continue;
			}
			while(r<qr) cnt1[a[++r]]++,now=max(now,cnt1[a[r]]);
			temp=now;
			while(l>ql) cnt1[a[--l]]++,now=max(now,cnt1[a[l]]);
			ans[q[i].id]=(now>=(qr-ql+1)/2+1?1:0);
			now=temp;
			while(l<br[j]+1) cnt1[a[l++]]--; 
		}
	}
	rep(i,1,m)
	{
		if(ans[i]) puts("usable");
		else puts("unusable");
	}
} 

int main()
{
   // freopen("1.in","r",stdin);
	init();
	solve();
    return (0^_^0);
}
//以吾之血,铸吾最后的亡魂.

Sum and Product

队友牛逼带我超神....
首次看这个题,题意很好理解就是找到有多少个子区间,使得这个区间里所有元素的乘积等于和。...
看起来好复杂...但仔细思考一下,全部元素的乘积,...,这玩意是不是有点大....连续64个2想乘就爆longlong了,那全部元素的和的大小范围呢?\(n\)\(2e5\),\(a_i\)\(1e9\),也就是说这里的元素和最大值为\(2e14\),那乘积超过这个不就一定不合法了吗?那直接暴力,然后break。T了....为啥会T啊...昂,对了序列中可能有很多的1使得乘积长时间不变,那我们考虑能不能直接跳过这些乘积,直接和有效的数组想乘,我这里记了一个id[i]数组表示i之后一个非1的位置,然后固定左端点,寻找去合法右端点的数目,由于中间全部是1,累乘的结果很好维护,累加的话根据下标计算也很容易。考虑这之间有没有可能会出现合法的右端点,由于全部是1,所以乘积不变,和一直加,若之前和<乘积。并且这里1的个数又足够多的话,我们就能在一堆1中实现和=积的情况,并且最多一次,因为相等之后,和还往上加,积就不变,一定不和法,那这样就好办了,我们在跳的一堆1中看有没有符合条件的,跳过后看有没有符合条件。这样最多跳64次。总复杂度为\(O(nlogn)\)
注意这里用除法代替乘法,避免爆long long.

//不等,不问,不犹豫,不回头.
#include<bits/stdc++.h>
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=2e5+10;
const ll MAX=2e14;
int n,a[N],id[N],now;

inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}

int main()
{
    //freopen("1.in","r",stdin);
	get(n);
	rep(i,1,n) get(a[i]);
	int now=n;
	fep(i,n,1) 
	{
		id[i]=now;
		if(a[i]!=1) now=i;
	}
	id[n]=0;
	int ans=0;
	rep(i,1,n) //统计每个左端点合法的右端点 
	{
		ll mt=a[i],sm=a[i];
		now=i;
		while(id[now])
		{
			if(MAX/mt<a[id[now]]) break;
			if(sm<mt&&sm+(id[now]-now-1)>=mt) ++ans;
			mt=mt*a[id[now]];
			sm=sm+a[id[now]]+(id[now]-now-1);
			if(mt==sm) ++ans;
			now=id[now];
		}
	}
	put(ans);
    return (0^_^0);
}
//以吾之血,铸吾最后的亡魂.

J True/False Worksheet

这个题主要是思路被限制住了,想到并查集后就完全想着用数学方法解决问题了...
其实看到\(n\)\(5000\)的时候就可以考虑\(dp\)的写法了。
首先考虑无解的情况,就是一个要求不相同的区间里面的数被迫相同,我们直接预处理就行。
考虑\(DP\)怎样设状态,由于题目中的限制条件是一个区间必须是相同或不能全部相同,所以我们状态中就应该能体现这个相同与否的状态。考虑设\(f[i][j]\)表示从第\(j\)位到第\(i\)位都相同,且到\(i\)位结尾的方案数。转移时可以想不用考虑限制条件。当第\(i\)个和第\(i-1\)个保持一样时\(f[i][j]=f[i-1][j]\),当第\(i\)个和\(i-1\)不一样时,\(f[i][i]=\)\(\sum_{k=1}^{i-1}{f[i-1][k]}\)。那么接下来考虑相同的限制条件,比如说从l到i都必须是相同的,那么\(f[i][1],f[i][2]...f[i][l]\)都是合法的状态,考虑不相同的限制条件,比如从l到i都不相同,那么从\(f[i][l+1],f[i][l+2],...,f[i][i]\)都是合法的,既然如此我们就可以得知每个i的所有合法状态有哪些即\(diff[i]+1-same[i]\)这些状态是合法的。至于当\(f[i][i]\)转移时我们可以对\(i-1\)的转态枚举时,可以直接从\(1\)\(i-1\),因为在\(i-1\)时我们只保存了合法的状态。同理在统计答案时,我们可以直接求和,不必在乎是否合法,因为我们只保存了合法的状态的值,能转移过来的一定是合法的。

//不等,不问,不犹豫,不回头.
#include<bits/stdc++.h>
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=5010;
int n,m,f[N][N];//f[i][j]表示从j到i都相同的方案数
int same[N],diff[N]; 
bool flag=true;
char c[N];

inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}

inline void init()
{
	get(n);get(m);
	rep(i,1,n) same[i]=i;
	rep(i,1,m)
	{
		int get(x),get(y);
		scanf("%s",c+1);
		if(c[1]=='s') same[y]=min(same[y],x);
		else diff[y]=max(diff[y],x);
	}
	rep(i,1,n) if(diff[i]>=same[i]) flag=false;
	if(!flag) puts("0");
}

inline void solve()
{
	f[1][1]=2;
	rep(i,2,n) 
	{
		rep(j,diff[i]+1,same[i]) f[i][j]=(f[i][j]+f[i-1][j])%P;
		if(same[i]==i)
		{
			rep(j,1,i-1) f[i][i]=(f[i][i]+f[i-1][j])%P;
		}
	}
	int ans=0;
	rep(i,1,n) ans=(ans+f[n][i])%P;	
	put(ans);
}

int main()
{
    //freopen("1.in","r",stdin);
	init();
	if(flag) solve();
    return (0^_^0);	
}
//以吾之血,铸吾最后的亡魂.

posted @ 2021-08-19 20:57  逆天峰  阅读(78)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//