今天这个成绩还是不怎么理想,没能A掉T1,实乃一大遗憾
T1:盘王节
其实这道题没什么思维难度,主要就是注意细节。
进攻有 2 2 2种情况:
1. 1. 1.先全部打御符,用尽量小的去打,即大于等于它最小的那个,打完后,只剩兵符,此时有又有 2 2 2种选择,若兵符大于 0 0 0则产生负贡献,就直接把它看做 0 0 0,若它小于 0 0 0且小于攻击的兵符,则产生正贡献,就打兵符。
2. 2. 2.贪心打兵符(不存在边打兵符,边打御符,若要打御符,还不如先打完) 用最大的攻击兵符去打最小的,这样会产生更大的贡献。
考场思路:由绝对值小于等于 100 100 100变相想到用桶来储存,先 s o r t sort sort,再 u n i q u e unique unique一下,接下来 l o w e r lower lower_ b o u n d bound bound查找,但这样极大地增加了时间的复杂度,而且我打完御符后就没打负的兵符了,贪心时也有部分细节没注意,望改进。
反思:
- 过于信任大样例,没有仔细检查;
- 思路不严密,问题想了一半就走人了
做题和人生都是这样
不要妄自菲薄也不要盲目自信
要看到希望却又不忘记安危(来自 F s y Fsy Fsy巨佬的鸡汤)
代码:
#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
#define N 1000005
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
struct node
{
int num,val;
bool operator < (cs node &a) cs
{
return val<a.val;
}
}a[N],b[N],c[N],d[N],e[N],f[N];
int n,m1,m2,ans,sum;
bool mark;
void work()
{
int j=n;
for(re int i=1;i<=m2;++i)
{
while(j&&c[i].num>a[j].num&&c[i].val<=a[j].val)
{
sum+=(a[j].val-c[i].val)*a[j].num;
c[i].num-=a[j].num;
a[j].num=0;
--j;
}
if(!j||c[i].val>a[j].val) break;
sum+=(a[j].val-c[i].val)*c[i].num;
a[j].num-=c[i].num;
c[i].num=0;
}
ans=max(ans,sum);
}
void work2()
{
int j=1;
sum=0;
for(re int i=1;i<=m1;++i)
{
while(j<n&&a[j].val<b[i].val) ++j;
while(j<n&&b[i].num>a[j].num&&b[i].val<=a[j].val)
{
b[i].num-=a[j].num;
a[j].num=0;
++j;
}
if(b[i].val>a[j].val)
{
mark=1;
break;
}
a[j].num-=b[i].num;
b[i].num=0;
if(j==n&&!a[j].num)
{
mark=1;
break;
}
}
j=n;
for(int i=1;i<=m2;++i)
{
if(c[i].val>=0) break;
while(j&&c[i].num>a[j].num&&c[i].val<=a[j].val)
{
sum+=(a[j].val-c[i].val)*a[j].num;
c[i].num-=a[j].num;
a[j].num=0;
--j;
}
if(!j||c[i].val>a[j].val) break;
sum+=(a[j].val-c[i].val)*c[i].num;
a[j].num-=c[i].num;
c[i].num=0;
}
if(!mark) for(re int i=1;i<=n;++i) if(a[i].val>0) sum+=a[i].val*a[i].num;
ans=max(ans,sum);
}
signed main()
{
n=read();
m1=read();
m2=read();
for(re int i=1;i<=n;++i) a[i].val=read(),a[i].num=read();
for(re int i=1;i<=m1;++i) b[i].val=read(),b[i].num=read();
for(re int i=1;i<=m2;++i) c[i].val=read(),c[i].num=read();
sort(a+1,a+1+n);
sort(b+1,b+1+m1);
sort(c+1,c+1+m2);
for(re int i=1;i<=n;++i) d[i]=a[i];
for(re int i=1;i<=m1;++i) e[i]=b[i];
for(re int i=1;i<=m2;++i) f[i]=c[i];
work();
swap(a,d),swap(b,e),swap(c,f);
work2();
printf("%lld",ans);
return 0;
}
T2:祝著节
题意:给你一个联通的无向图,找出所有权值和大于等于 X X X的最(次)小生成树的方案数,且树内必须至少有 2 2 2条边颜色不同,且最小的权值和必须等于 X X X。
思路:(部分内容来自 w y h wyh wyh巨佬 o r z orz orz)
首先有一个结论必须推出:我们得到的所有方案中的最小生成树与原图中最小生成树的形态有且仅有一条边不同。
证明:假设已求出一棵纯色的最小生成树,我们加入一条另外一种颜色的树外边,并拆掉构成的环中最大的那条边,此时方案已经合法,若我们再去加边拆边,则无意义。
接着,对于每条非树边,我们求出强制包含其的不严格次小生成树,利用树上倍增来求(需熟练掌握),环上减去最大边即可。
对于次小生成树的权值,分两种情况讨论。
如果该次小生成树权值等于 X X X,记为 e q u + + equ++ equ++。如果小于,记为 l e s + + les++ les++,大于不管。
然后对于最初的 s u m sum sum,也要讨论。
如果最初的 s u m sum sum已经等于 X X X,那新加的边也只能是 e q u equ equ。
根据刚刚结论,有 n − 1 n-1 n−1条最小生成树边同色,而剩下的边中,有 e q u equ equ条边,这些边不能全都和 M S T MST MST同色。
容斥一下,总方案减去非法方案(即 e q u + n − 1 equ+n-1 equ+n−1条边都同色),得到
A n s = 2 m − 2 ∗ ( 2 m − ( n − 1 ) − e q u ) A n s=2^{m}-2 *\left(2^{m-(n-1)-e q u}\right) Ans=2m−2∗(2m−(n−1)−equ)
这个 2 2 2是因为颜色可以同时取反。
如果 s u m sum sum小于 X X X,那我们在取能使答案合法的 e q u equ equ的同时,那些 l e s les les边都要和 M S T MST MST同色,否则答案不会取 e q u equ equ。
使用容斥, A n s = 2 ∗ ( 2 m − ( n − 1 ) − l e s − 2 m − ( n − 1 ) − l e s − e q u ) A n s=2 *\left(2^{m-(n-1)-l e s}-2^{m-(n-1)-l e s-e q u}\right) Ans=2∗(2m−(n−1)−les−2m−(n−1)−les−equ)
即可得出答案。
代码:
#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
#define mod 1000000007
#define N 200003
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
struct node
{
int from,to,w,flag;
bool operator < (cs node &a)cs
{
return w<a.w;
}
}edge[N];
int first[N],equ,les,fa[N],f[N][22],maxx[N][22],net[N],too[N],ww[N];
int dep[N];
int tot,t,n,m,X,x,y,z;
void clear()
{
tot=0;
memset(first,0,sizeof(first));
for(re int i=1;i<=n;++i) fa[i]=i;
}
int getfa(int x)
{
if(fa[x]!=x) fa[x]=getfa(fa[x]);
return fa[x];
}
void dfs(int x,int faa)
{
dep[x]=dep[faa]+1;
f[x][0]=faa;
for(re int i=1;i<=20;++i) f[x][i]=f[f[x][i-1]][i-1];
for(re int i=1;i<=20;++i) maxx[x][i]=max(maxx[x][i-1],maxx[f[x][i-1]][i-1]);
for(re int e=first[x];e;e=net[e])
{
int v=too[e];
if(v==faa) continue;
maxx[v][0]=ww[e];
dfs(v,x);
}
}
void add(int x,int y,int z)
{
net[++tot]=first[x];
first[x]=tot;
too[tot]=y;
ww[tot]=z;
}
int quick(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
int query(int a,int b)
{
int gu=0;
if(dep[a]<dep[b]) swap(a,b);
for(int i=20;i>=0;i--)
{
if(dep[f[a][i]]>=dep[b])
{
gu=max(gu,maxx[a][i]);
a=f[a][i];
}
}
if(a==b) return gu;
for(int i=20;i>=0;--i)
{
if(f[a][i]!=f[b][i])
{
gu=max(gu,max(maxx[a][i],maxx[b][i]));
a=f[a][i];
b=f[b][i];
}
}
return max(gu,max(maxx[a][0],maxx[b][0]));
}
int cnt,sum,ans;
void solve()
{
cnt=sum=ans=equ=les=0;
for(re int i=1;i<=m;++i)
{
x=getfa(edge[i].from);
y=getfa(edge[i].to);
if(x!=y)
{
++cnt;
sum+=edge[i].w;
fa[x]=y;
edge[i].flag=1;
add(edge[i].from,edge[i].to,edge[i].w);
add(edge[i].to,edge[i].from,edge[i].w);
if(cnt==n-1) break;
}
}
if(sum>X)
{
puts("0");
return;
}
dfs(1,0);
int now=sum;
for(re int i=1;i<=m;++i)
{
if(!edge[i].flag)
{
now=sum+edge[i].w-query(edge[i].from,edge[i].to);
if(now==X) ++equ;
if(now<X) ++les;
}
}
if(sum==X) ans+=(quick(2,m)-2*quick(2,m-n+1-equ)%mod+mod)%mod,ans%=mod;
else ans+=2*(quick(2,m-n+1-les)-quick(2,m-n+1-equ-les)+mod)%mod,ans%=mod;
printf("%lld\n",ans);
}
signed main()
{
t=read();
while(t--)
{
n=read();
m=read();
X=read();
clear();
for(re int i=1;i<=m;++i)
{
edge[i].flag=0;
edge[i].from=read();
edge[i].to=read();
edge[i].w=read();
}
sort(edge+1,edge+1+m);
solve();
}
return 0;
}
T3:耍望节
咕咕咕