T1-文件改名
你现在有 \(n\) 个文件名不同的文件,要更改这 \(n\) 个文件的文件名
一次可以修改一个文件的文件名,要求中途不能有文件名相同
问最少修改几次
解法
可以发现,分三种情况
如果是自环,啥都不用改
如果不在环上,顺着直接改就好了
如果在环上,需要先改一次使环断开
ac代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define M1 19260817
#define M2 20040220
#define N 100010
using namespace std;
map<pii,int>P;
pii tmp;
int n,cnt,l,tmp1,tmp2,tg,ans,a[N],b[N],vis[N],rd[N],id[N<<1];
char str1[20],str2[20];
int dfs(int u)
{
if(vis[u])tg=1;
else
cnt++,vis[u]=1,(id[b[u]])&&(dfs(id[b[u]]));
}
int main()
{
freopen("files.in","r",stdin);
freopen("files.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s%s",str1+1,str2+1);
tmp1=tmp2=0,l=strlen(str1+1);
for(int i=1;i<=l;i++)
tmp1=(tmp1*233ll+str1[i])%M1,
tmp2=(tmp2*2333ll+str1[i])%M2;
tmp=mp(tmp1,tmp2);
(!P.count(tmp))&&(P[tmp]=++cnt);
a[i]=P[tmp],id[P[tmp]]=i;
tmp1=tmp2=0,l=strlen(str2+1);
for(int i=1;i<=l;i++)
tmp1=(tmp1*233ll+str2[i])%M1,
tmp2=(tmp2*2333ll+str2[i])%M2;
tmp=mp(tmp1,tmp2);
(!P.count(tmp))&&(P[tmp]=++cnt);
b[i]=P[tmp];
}
for(int i=1;i<=n;i++)
(id[b[i]])&&(rd[id[b[i]]]=1);
for(int i=1;i<=n;i++)
(!rd[i])&&(cnt=0,dfs(i),ans+=cnt);
for(int i=1;i<=n;i++)
(!vis[i]&&a[i]!=b[i])&&(tg=cnt=0,dfs(i),ans+=cnt+tg);
printf("%d\n",ans);
return 0;
}
T2-怪物猎人
有 \(n\) 个怪物,每个怪物有两个属性 \(A_i,B_i\)
杀死一个怪物需要 \(A_i×B_i\) 的血量
现在给你m个人,每个人分别有 \(h_i\) 的血量
每杀死一个怪物,其它怪物的两个属性都会上升 \(d\)
问你每个人最多能杀死多少怪物
解法
杀死一个怪物的耗血 \(H=(A+kd)(B+kd)=AB+kd(A+B)+k^2d^2\)
所以杀一堆怪时,最优策略是先杀 \(A_i+B_i\) 较大的
我们根据 \(A_i+B_i\) 来排序
设 \(f_{i,j}\) 表示考虑到第i个怪物,有j个怪物不杀
需要的最少血量,转移的话推一下删掉一个怪物会减少的血量即可
还有一种更好的DP
设 \(f_{i,j}\) 表示考虑到第i个怪物,取j个怪物需要的最少血量
那么 \(f_{i,j}=min(f_{i-1,j},f_{i-1,j-1}+(A_i+(j-1)d)(B_i+(j-1)d))\)
代码懒得写了,放过我吧QAQ
ac代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node
{
int s;ll g;
bool operator<(const node&a)const{return s>a.s;}
}a[3010];
int n,m,d,x[3010],y[3010];
ll s[3010],ans[3010],f[3010][3010],h[300010];
int main()
{
freopen("hunter.in","r",stdin);
freopen("hunter.out","w",stdout);
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=n;i++)scanf("%d",&x[i]);
for(int i=1;i<=n;i++)scanf("%d",&y[i]);
for(int i=1;i<=n;i++)a[i]={x[i]+y[i],1ll*x[i]*y[i]};
for(int i=1;i<=m;i++)scanf("%lld",&h[i]);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
f[0][0]+=a[i].g+1ll*(i-1)*a[i].s*d+1ll*(i-1)*(i-1)*d*d;
for(int i=n;i>=1;i--)s[i]=s[i+1]+a[i].s;
for(int i=1;i<=n;i++)
{
f[i][0]=f[i-1][0],f[i][i]=f[i-1][i-1]-a[i].g-s[i+1]*d-1ll*(n-i)*(n-i)*d*d;
for(int j=1;j<i;j++)
f[i][j]=min(f[i-1][j],f[i-1][j-1]-a[i].g-1ll*(i-j)*a[i].s*d-s[i+1]*d-1ll*(n-j)*(n-j)*d*d);
}
for(int i=1;i<=n;i++)ans[i]=f[n][n-i];
for(int i=1;i<=m;i++)
printf("%d ",lower_bound(ans+1,ans+n+1,h[i])-ans-1);
return 0;
}
T3-魔法帽游戏
你有一个长度为 \(n\) 的数组,初始时 \(A_i=i\)
你还有一个长度为 \(m\) 的操作序列,每次操作 \(a_i,b_i\) 表示交换这两个位置上的数
你有 \(q\) 个询问,每次询问 \(x,l,r\) 表示顺序做了 \([l,r]\) 区间内的操作后数 \(x\) 在哪里
解法
看到这个题,前几天学的莫队终于有用了!
这不就是莫队的板子题吗~嘤嘤嘤~
关于莫队,详见学习笔记#莫队
那么指针移动所带来的影响是什么呢
对于l指针,不管是左移还是右移,都是交换一次
那交换什么呢,我们考虑到这是一开始就做的交换
所以要对于值交换,也就是将当前状态中值为 \(a_l,b_l\) 的数交换
对于r指针,不管左移右移,也是交换一次
因为是最后做的交换,所以直接对位置交换即可
具体为将当前状态中位置为 \(a_r,b_r\) 的数交换
然后用莫队暴力搞一下就愉快的AC啦
ac代码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define N 200010
#define reg register
using namespace std;
namespace fast_IO
{
const int IN_LEN = 10000000, OUT_LEN = 10000000;
char ibuf[IN_LEN], obuf[OUT_LEN], *ih = ibuf + IN_LEN, *oh = obuf, *lastin = ibuf + IN_LEN, *lastout = obuf + OUT_LEN - 1;
inline char getchar_(){return (ih == lastin) && (lastin = (ih = ibuf) + fread(ibuf, 1, IN_LEN, stdin), ih == lastin) ? EOF : *ih++;}
inline void putchar_(const char x){if(oh == lastout) fwrite(obuf, 1, oh - obuf, stdout), oh = obuf; *oh ++= x;}
inline void flush(){fwrite(obuf, 1, oh - obuf, stdout);}
inline void read(reg int&x)
{
char c=getchar_();
for(;!isdigit(c);c=getchar_());
for(;isdigit(c);c=getchar_())x=x*10+c-'0';
}
inline void write(reg int x){if(x>9)write(x/10);putchar_(x%10+'0');}
inline void writeln(reg int x){write(x),putchar_('\n');}
};
using namespace fast_IO;
int n,m,q,K,l,r,x[N],y[N],T[N],g[N],p[N],ans[N];
struct node
{
int q,l,r,id;
inline void init(reg int i){read(q),read(l),read(r),id=i;}
bool operator<(const node&a)const
{return T[l]==T[a.l]?((T[l]&1)?r<a.r:r>a.r):l<a.l;}
}a[200010];
inline void upd1(reg int i){swap(p[x[i]],p[y[i]]),swap(g[p[x[i]]],g[p[y[i]]]);}
inline void upd2(reg int i){swap(g[x[i]],g[y[i]]),swap(p[g[x[i]]],p[g[y[i]]]);}
int main()
{
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
read(n),read(m),read(q),K=sqrt(m);
for(reg int i=1;i<=n;++i)g[i]=p[i]=i;
for(reg int i=1;i<=m;++i)
read(x[i]),read(y[i]),T[i]=(i-1)/K+1;
for(reg int i=1;i<=q;++i)a[i].init(i);
sort(a+1,a+q+1),l=1,r=0;
for(reg int i=1;i<=q;++i)
{
while(l>a[i].l)upd1(--l);
while(r<a[i].r)upd2(++r);
while(l<a[i].l)upd1(l++);
while(r>a[i].r)upd2(r--);
ans[a[i].id]=p[a[i].q];
}
for(reg int i=1;i<=q;++i)writeln(ans[i]);
flush();return 0;
}