大意:
n个人,每一个人有一个初始值和一个想要修改的值,两者保证不同。每次只能修改一个人的值,且不能与其他人的值重复。问能否满足所有人的要求
思路:
显然,判环即可
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n;
map<string,ll> mp;
struct ty
{
string a,b;
}mas[N];
ll ne[N];
ll vis[N];
ll cn=0;
void dfs(ll id)
{
vis[id]=cn;
ll nex=ne[id];
if(nex==0) return;
if(vis[nex]!=cn) dfs(nex);
else
{
cout<<"No"<<endl;
exit(0);
}
}
void solve()
{
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>mas[i].a>>mas[i].b;
mp[mas[i].a]=i;
}
for(int i=1;i<=n;++i)
{
ll id=mp[mas[i].b];
ne[i]=id;
}
for(int i=1;i<=n;++i)
{
// memxet(vis,0,sizeof vis);
if(!vis[i]) cn++,dfs(i);
}
cout<<"Yes"<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
solve();
return 0;
}
大意:
一周有n天,给出一个数组a,现在设计这一周的休息日的安排(至少一个),并计算每周的价值。每天的价值:休息日没价值,工作日i的价值为a[min(i1,i2)],i1表示i至少往前几天有休息日,i2表示i至少往后几天有休息日,n<5000
思路:
气死我啦气死我啦,一心当成一周七天来算,然后写了个状压。。。
会发现如果考虑如何安排的话,我们要关注休息日的数量和分布,这是很麻烦的
所以我们dp(只有玄学可以压制玄学)
不妨设dp[i]表示前i天中第i天恰好为休息日的最大价值。则
dp[i]=max(dp[i],dp[j]+co(i-j-1));//1<=j<i,co(x)代表区间长度为x,且两端(不包含)都是休息日的总价值
显然co可以预处理然后O(1)解决
还有一个细节就是第n天不一定是休息日最优。注意到一周是固定的,我们不妨转一转,将某个休息日作为第一天(显然不会影响最终结果),那么答案就是dp[n+1]了
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n;
ll mas[N];
ll sum[N];
ll co(ll x)
{
return sum[(x+1)/2]+sum[x/2];
}
ll dp[N];
void solve()
{
cin>>n;
for(int i=1;i<=n;++i) cin>>mas[i],sum[i]=sum[i-1]+mas[i];
dp[1]=0;
for(int i=1;i<=n+1;++i)
{
for(int j=1;j<i;++j)
{
dp[i]=max(dp[i],dp[j]+co(i-j-1));
}
}
cout<<dp[n+1]<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
solve();
return 0;
}
大意:
给定一个字符串s,提供两种操作:
1 修改某个位置的字符
2 给定l,r,判断区间l,r对应的子串是否是s排序后的新字符串t的子串
思路:
考虑一下第二个操作,如果想要答案为真,我们需要满足以下两个条件:
区间中出现的字母一定是按顺序出现的,这个显然
以及中间(不包括两端)的字母在[l,r]出现的次数=在s中出现的总次数,否则s排序后一定会有新字母加进来
这是我们需要的两个条件
不难发现第二个条件是很好维护的,开26个树状数组就行了。那么对于第一个条件,只要我们在check的时候按字母顺序去跑,并更新当前位置,就行了。
tip:同一个位置可能多次修改,所以操作一弄完后对原字符串也要更新一下(应该只有我会漏过这个细节吧。。。)
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n,q;
char s[N];
ll a,b,c;
ll l,r;
char d;
struct ty
{
ll tr[N];
ll low(ll x)
{
return x&(-x);
}
void add(ll x,ll y)
{
while(x<=n)
{
tr[x]+=y;
x+=low(x);
}
}
ll sum(ll x)
{
ll ans=0;
while(x)
{
ans+=tr[x];
x-=low(x);
}
return ans;
}
ll get_sum(ll l,ll r)
{//返回区间字母个数
return sum(r)-sum(l-1);
}
}tre[27];
void sol()
{
cin>>a;
if(a==1)
{
cin>>b>>d;
tre[s[b]-'a'].add(b,-1);
s[b]=d;
tre[d-'a'].add(b,1);
return;
}
cin>>l>>r;
int ma=0,mi=26;
for(int i=0;i<26;++i)
{
if(tre[i].get_sum(l,r)>0)
{
ma=max(ma,i);
mi=min(mi,i);
}
}
// cout<<" sf "<<mi<<' '<<ma<<endl;
//有就先塞进来
ll x=l;
for(int i=mi;i<=ma;++i)
{
//中间的数字
//一个要求是其所有都在区间内出现
ll det=tre[i].get_sum(l,r);
if(i!=mi&&i!=ma)
{
if(det<tre[i].get_sum(1,n))
{
cout<<"No"<<endl;
return;
}
}
if(tre[i].get_sum(x,x+det-1)!=det)
{
cout<<"No"<<endl;
return;
}
x+=det;
}
cout<<"Yes"<<endl;
}
void solve()
{
cin>>n;
cin>>(s+1);
cin>>q;
for(int i=1;i<=n;++i)
{
tre[s[i]-'a'].add(i,1);
}
// for(int i=0;i<26;++i)
// {
// cout<<(char)('a'+i)<<' '<<tre[i].get_sum(1,3)<<endl;
// }
while(q--)
{
sol();
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
solve();
return 0;
}
大意:
给定一个图,有两种拼图:1*1,1*2,图中的1代表该位置是一个1*1的拼图,2代表1*2的拼图,?代表随便。现在问该图是否有合法的还原(不同拼图不能重叠)
思路:
标1的位置显然不用管。只考虑剩下的点,并将相邻点连边的话,显然是一张二分图,我们的目标其实就是要使得每一个标2的位置有一个匹配,剩下的?位置填1就行了。
对二分图跑网络流的话,就是要让有2的路径流量为1
那么最大流就好了