国庆模拟赛二总结
垃圾梦熊服务器真烂
T1
非常简单的题,一眼扫描线,但是™的扫描线竟然会TLE,只有80pts,我是真的服了(虽然正解简单到爆)。
正解好像是珂以从末开始直接一遍 循环扫过去每次加最大值就可以了。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long 不开longlong见祖宗
#define ls now<<1
#define rs now<<1|1
const ll N=20000005,M=1919810,inf=1145141919;
ll n,maxn[N];
struct xx{
ll x,y;
}a[N];
int main(){
//freopen("unija.in","r",stdin);
//freopen("unija.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i].x>>a[i].y,a[i].x/=2,a[i].y/=2,maxn[a[i].x]=max(maxn[a[i].x],a[i].y);
//sort(a+1,a+n+1,cmp);
ll ans=0,y=0;
for(int i=1e7;i>=1;--i)
y=max(y,maxn[i]),ans+=y;
cout<<ans*4;
return 0;
}
T2
这个题赛时推了个前真后假的结论,喜提15pts,本来也想到冰茶姬了结果一开始的方向就错了/ng
其实这题就是一个简单的权值并查集!甚至都和树的形态没有关系!只需要将题目的条件转化成 就行了,其中 表示i到根的边权异或前缀和。
而且并查集维护的是点而不是我想的边,最后看并查集里面有几个根结果就是 。还要注意特判无解的情况。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
const ll N=1145140,M=1919810,mod=1e9+7;
ll n,Q;
ll qpow(ll a,ll b){
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
ll f[N],dis[N];
ll find(ll x){
if(x==f[x]) return x;
else{
ll pos=f[x];
f[x]=find(f[x]);
dis[x]=dis[x]^dis[pos];
return f[x];
}
}
void merge(ll x,ll y,ll z){
ll a=find(x),b=find(y);
f[b]=a;
dis[b]=dis[x]^dis[y]^z;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
ll ans=0,cnt=0;
cin>>n>>Q;
for(int i=1;i<n;++i){
ll a,b;
cin>>a>>b;
//add(a,b),add(b,a); 甚至不需要建树
}
for(int i=1;i<=n;++i) f[i]=i; //千万别忘了
for(int i=1;i<=Q;++i){
ll u,v,w;
cin>>u>>v>>w;
ll x=find(u),y=find(v);
if(x==y&&dis[u]!=(dis[v]^w)){ //如果重复出现同一对点且w不同,则无解
cout<<0;
return 0;
}
if(x!=y) merge(u,v,w);
}
for(int i=1;i<=n;++i)
if(find(i)==i) ++cnt;
cout<<qpow(2,cnt-1);
return 0;
}
T3
dp,推不来,只能说还是能力的问题了,还是得多做题而且要精做题,每写一道都得整一个题解写起,确保自己完全懂了回了并且下一次(虽然大概率没有)就能用上一些思维trick。
回到题目,先讨论70pts的dp。首先设 为处理完前i个任务,机器一还剩j时间的工作,机器二还剩k时间的工作时的最小工作时间(这个状态就已经是我设不出来的东西了/kel)。
转移:第i个任务是由 转移而来的,那么在第一台机器上的转移便是 。
其中若机器一需要的时间更多,需要考虑特殊情况:if(j>=k) cost=a[i],t1=a[i],t2=0;
或者机器二需要的时间多:if(j+a[i]<=k) cost=0,t1=j+a[i],t2=k;
一般的转移便是cost=j+a[i]-k,t1=j+a[i]-k,t2=0;
特殊情况下:如果第⼀台机器的完成时间已经⽐第⼆台要晚了,这时仍然把任务交个第⼀台机器的时候,两台机器的时间差就变为了 ,⽽不是在原有的 的基础上加上 ,原因是需要等第⼀台的上⼀个任务完成,才能在第⼆台机器上开始任务,这样时间才不会错。(题解说的)
机器二的转移和这里类似。总的复杂度就是 , 表示完成某个任务的最长时间。
剩下30pts要求只能 ,考虑将后两维通过他们之间的关系压缩成一维。
那么可以这样设: 为完成前i个任务,机器一剩余工作时间比机器二剩余工作时间多了j时间的机器一最小工作时间。机器二的工作时间珂以通过 来计算。
还要注意j可能为负数,所以转移的时候加上一个花费时间最大值M就可以了。
大致就是这样,具体思维过程还需要进一步体会啊。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
const ll N=3003,M=1919810,mod=1e9+7;
ll n;
struct xx{
ll ta,tb;
}a[N];
ll dp[N][2*N+5];
int main(){
freopen("task.in","r",stdin);
freopen("task.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i].ta>>a[i].tb;
memset(dp,63,sizeof(dp)); dp[0][N]=0;
for(int i=1;i<=n;++i)
for(int j=0;j<=2*N;++j){ //避免差值为负加了个N
if(j>=N){
dp[i][N+a[i].ta]=min(dp[i][N+a[i].ta],dp[i-1][j]+a[i].ta);
dp[i][j-a[i].tb]=min(dp[i][j-a[i].tb],dp[i-1][j]);
}
else{
dp[i][N-a[i].tb]=min(dp[i][N-a[i].tb],dp[i-1][j]+(N-j));
dp[i][j+a[i].ta]=min(dp[i][j+a[i].ta],dp[i-1][j]+a[i].ta);
}
}
ll ans=mod;
for(int i=0;i<=2*N;++i) ans=min(ans,dp[n][i]+max(0ll,N-i));
cout<<ans;
return 0;
}
T4
说实话不想补了,一开始连样例都模拟不出来。正解是哈希,但我竟然不会哈希,所以先不管它了。
摆了,去写图论了。